Reformat code

This commit is contained in:
Anatolii Cherednichenko 2022-08-30 02:11:28 +03:00
parent 3ddb370718
commit 55534ea002
140 changed files with 46068 additions and 48277 deletions

View file

@ -14,68 +14,66 @@
// Afrikaans localization file // Afrikaans localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace afrikaans // language namespace
{ {
namespace afrikaans // language namespace // language name in lowercase
{ static std::string language = "afrikaans";
// language name in lowercase
static std::string language = "afrikaans";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"failed", "Het misluk"}, {"failed", "Het misluk"},
{"unknown", "onbekend"}, {"unknown", "onbekend"},
{"Tunnels", "Tonnels"}, {"Tunnels", "Tonnels"},
{"I2P tunnels", "I2P tonnels"}, {"I2P tunnels", "I2P tonnels"},
{"SAM sessions", "SAM sessies"}, {"SAM sessions", "SAM sessies"},
{"OK", "LEKKER"}, {"OK", "LEKKER"},
{"Testing", "Besig om te toets"}, {"Testing", "Besig om te toets"},
{"Firewalled", "Vuurmuur'd"}, {"Firewalled", "Vuurmuur'd"},
{"Unknown", "Onbekend"}, {"Unknown", "Onbekend"},
{"Error", "Fout"}, {"Error", "Fout"},
{"Offline", "Aflyn"}, {"Offline", "Aflyn"},
{"Uptime", "Optyd"}, {"Uptime", "Optyd"},
{"Network status", "Netwerk status"}, {"Network status", "Netwerk status"},
{"Network status v6", "Netwerk status v6"}, {"Network status v6", "Netwerk status v6"},
{"Family", "Familie"}, {"Family", "Familie"},
{"Received", "Ontvang"}, {"Received", "Ontvang"},
{"Sent", "Gestuur"}, {"Sent", "Gestuur"},
{"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."}, {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."},
{"Router Ident", "Router Ident"}, {"Router Ident", "Router Ident"},
{"Router Family", "Router Familie"}, {"Router Family", "Router Familie"},
{"Enabled", "Geaktiveer"}, {"Enabled", "Geaktiveer"},
{"Disabled", "Gedeaktiveer"}, {"Disabled", "Gedeaktiveer"},
{"Change", "Verander"}, {"Change", "Verander"},
{"Change language", "Verander taal"}, {"Change language", "Verander taal"},
{"Description", "Beskrywing"}, {"Description", "Beskrywing"},
{"Submit", "Stuur"}, {"Submit", "Stuur"},
{"Proxy error", "Proxy-fout"}, {"Proxy error", "Proxy-fout"},
{"Host", "Gasheer"}, {"Host", "Gasheer"},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"dag", "dae"}}, {"days", {"dag", "dae"}},
{"hours", {"uur", "ure"}}, {"hours", {"uur", "ure"}},
{"minutes", {"minuut", "minute"}}, {"minutes", {"minuut", "minute"}},
{"seconds", {"seconde", "sekondes"}}, {"seconds", {"seconde", "sekondes"}},
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -14,202 +14,200 @@
// Armenian localization file // Armenian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace armenian // language namespace
{ {
namespace armenian // language namespace // language name in lowercase
{ static std::string language = "armenian";
// language name in lowercase
static std::string language = "armenian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "ԿիԲ"}, {"KiB", "ԿիԲ"},
{"MiB", "ՄիԲ"}, {"MiB", "ՄիԲ"},
{"GiB", "ԳիԲ"}, {"GiB", "ԳիԲ"},
{"building", "կառուցվում է"}, {"building", "կառուցվում է"},
{"failed", "Անհաջող"}, {"failed", "Անհաջող"},
{"expiring", "Լրանում է"}, {"expiring", "Լրանում է"},
{"established", "կարգավոյված է"}, {"established", "կարգավոյված է"},
{"unknown", "անհայտ"}, {"unknown", "անհայտ"},
{"exploratory", "հետազոտոկան"}, {"exploratory", "հետազոտոկան"},
{"<b>i2pd</b> webconsole", "Վեբ-կոնսոլ <b>i2pd</b>"}, {"<b>i2pd</b> webconsole", "Վեբ-կոնսոլ <b>i2pd</b>"},
{"Main page", "Գլխավոր էջ"}, {"Main page", "Գլխավոր էջ"},
{"Router commands", "Երթուղիչի հրահանգներ"}, {"Router commands", "Երթուղիչի հրահանգներ"},
{"Local Destinations", "Տեղական վերջնակետերը"}, {"Local Destinations", "Տեղական վերջնակետերը"},
{"LeaseSets", "ԼիզՍեթեր"}, {"LeaseSets", "ԼիզՍեթեր"},
{"Tunnels", "Թունելներ"}, {"Tunnels", "Թունելներ"},
{"Transit Tunnels", "Տարանցիկ թունելներ"}, {"Transit Tunnels", "Տարանցիկ թունելներ"},
{"Transports", "Տրանսպորտ"}, {"Transports", "Տրանսպորտ"},
{"I2P tunnels", "I2P թունելներ"}, {"I2P tunnels", "I2P թունելներ"},
{"SAM sessions", "SAM նստաշրջաններ"}, {"SAM sessions", "SAM նստաշրջաններ"},
{"ERROR", "ՍԽԱԼ"}, {"ERROR", "ՍԽԱԼ"},
{"OK", "ԼԱՎ"}, {"OK", "ԼԱՎ"},
{"Testing", "Փորձարկում"}, {"Testing", "Փորձարկում"},
{"Firewalled", "Արգելափակված է դրսից"}, {"Firewalled", "Արգելափակված է դրսից"},
{"Unknown", "Անհայտ"}, {"Unknown", "Անհայտ"},
{"Proxy", "Պրոկսի"}, {"Proxy", "Պրոկսի"},
{"Mesh", "MESH-ցանց"}, {"Mesh", "MESH-ցանց"},
{"Error", "Սխալ"}, {"Error", "Սխալ"},
{"Clock skew", "Ոչ ճշգրիտ ժամանակ"}, {"Clock skew", "Ոչ ճշգրիտ ժամանակ"},
{"Offline", "Օֆլայն"}, {"Offline", "Օֆլայն"},
{"Symmetric NAT", "Սիմետրիկ NAT"}, {"Symmetric NAT", "Սիմետրիկ NAT"},
{"Uptime", "Առկայություն"}, {"Uptime", "Առկայություն"},
{"Network status", "Ցանցի կարգավիճակ"}, {"Network status", "Ցանցի կարգավիճակ"},
{"Network status v6", "Ցանցի կարգավիճակ v6"}, {"Network status v6", "Ցանցի կարգավիճակ v6"},
{"Stopping in", "Դադարում"}, {"Stopping in", "Դադարում"},
{"Family", "Խմբատեսակ"}, {"Family", "Խմբատեսակ"},
{"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"}, {"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"},
{"Received", "Ստացվել է"}, {"Received", "Ստացվել է"},
{"KiB/s", "ԿիԲ/վ"}, {"KiB/s", "ԿիԲ/վ"},
{"Sent", "Ուղարկվել է"}, {"Sent", "Ուղարկվել է"},
{"Transit", "Տարանցում"}, {"Transit", "Տարանցում"},
{"Data path", "Տվյալների ուղին"}, {"Data path", "Տվյալների ուղին"},
{"Hidden content. Press on text to see.", "Թաքցված բովանդակություն: Տեսնելու համար սեղմեկ տեքստին:"}, {"Hidden content. Press on text to see.", "Թաքցված բովանդակություն: Տեսնելու համար սեղմեկ տեքստին:"},
{"Router Ident", "Երթուղիչի նույնականացուցիչ"}, {"Router Ident", "Երթուղիչի նույնականացուցիչ"},
{"Router Family", "Երթուղիչի խումբը"}, {"Router Family", "Երթուղիչի խումբը"},
{"Router Caps", "Երթուղիչի հատկություններ"}, {"Router Caps", "Երթուղիչի հատկություններ"},
{"Version", "Տարբերակ"}, {"Version", "Տարբերակ"},
{"Our external address", "Մեր արտաքին հասցեն"}, {"Our external address", "Մեր արտաքին հասցեն"},
{"supported", "համատեղելի է"}, {"supported", "համատեղելի է"},
{"Routers", "Երթուղիչներ"}, {"Routers", "Երթուղիչներ"},
{"Floodfills", "Floodfills-ներ"}, {"Floodfills", "Floodfills-ներ"},
{"Client Tunnels", "Oգտատիրական թունելներ"}, {"Client Tunnels", "Oգտատիրական թունելներ"},
{"Services", "Ծառայություններ"}, {"Services", "Ծառայություններ"},
{"Enabled", "Միացված է"}, {"Enabled", "Միացված է"},
{"Disabled", "Անջատված է"}, {"Disabled", "Անջատված է"},
{"Encrypted B33 address", "Գաղտնագրված B33 հասցեներ"}, {"Encrypted B33 address", "Գաղտնագրված B33 հասցեներ"},
{"Address registration line", "Հասցեի գրանցման տող"}, {"Address registration line", "Հասցեի գրանցման տող"},
{"Domain", "Տիրույթ"}, {"Domain", "Տիրույթ"},
{"Generate", "Գեներացնել"}, {"Generate", "Գեներացնել"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b> Նշում. </b> արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (example.i2p): Ենթատիրույթներ գրանցելու համար խնդրում ենք օգտագործել i2pd-tools գործիքակազմը"}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b> Նշում. </b> արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (example.i2p): Ենթատիրույթներ գրանցելու համար խնդրում ենք օգտագործել i2pd-tools գործիքակազմը"},
{"Address", "Հասցե"}, {"Address", "Հասցե"},
{"Type", "Տեսակը"}, {"Type", "Տեսակը"},
{"EncType", "Գաղտնագրի տեսակը"}, {"EncType", "Գաղտնագրի տեսակը"},
{"Inbound tunnels", "Մուտքային թունելներ"}, {"Inbound tunnels", "Մուտքային թունելներ"},
{"ms", "մլվ"}, {"ms", "մլվ"},
{"Outbound tunnels", "Ելքային թունելներ"}, {"Outbound tunnels", "Ելքային թունելներ"},
{"Tags", "Թեգեր"}, {"Tags", "Թեգեր"},
{"Incoming", "Մուտքային"}, {"Incoming", "Մուտքային"},
{"Outgoing", "ելքային"}, {"Outgoing", "ելքային"},
{"Destination", "Նշանակման վայր"}, {"Destination", "Նշանակման վայր"},
{"Amount", "Քանակ"}, {"Amount", "Քանակ"},
{"Incoming Tags", "Մուտքային պիտակներ"}, {"Incoming Tags", "Մուտքային պիտակներ"},
{"Tags sessions", "Նստաշրջանի պիտակներ"}, {"Tags sessions", "Նստաշրջանի պիտակներ"},
{"Status", "Կարգավիճակ"}, {"Status", "Կարգավիճակ"},
{"Local Destination", "Տեղական նշանակման կետ"}, {"Local Destination", "Տեղական նշանակման կետ"},
{"Streams", "Հոսքեր"}, {"Streams", "Հոսքեր"},
{"Close stream", "Փակել հոսքը"}, {"Close stream", "Փակել հոսքը"},
{"I2CP session not found", "I2CP նստաշրջանը գոյություն չունի"}, {"I2CP session not found", "I2CP նստաշրջանը գոյություն չունի"},
{"I2CP is not enabled", "I2CP միացված է"}, {"I2CP is not enabled", "I2CP միացված է"},
{"Invalid", "Անվավեր"}, {"Invalid", "Անվավեր"},
{"Store type", "Պահեստավորման տեսակը"}, {"Store type", "Պահեստավորման տեսակը"},
{"Expires", "Սպառվում է"}, {"Expires", "Սպառվում է"},
{"Non Expired Leases", "Չսպառված Lease-եր"}, {"Non Expired Leases", "Չսպառված Lease-եր"},
{"Gateway", "Դարպաս"}, {"Gateway", "Դարպաս"},
{"TunnelID", "Թունելի ID"}, {"TunnelID", "Թունելի ID"},
{"EndDate", "Ավարտ"}, {"EndDate", "Ավարտ"},
{"not floodfill", "ոչ floodfill-ներ"}, {"not floodfill", "ոչ floodfill-ներ"},
{"Queue size", "Հերթի չափսը"}, {"Queue size", "Հերթի չափսը"},
{"Run peer test", "Գործարկել փորձարկումը"}, {"Run peer test", "Գործարկել փորձարկումը"},
{"Decline transit tunnels", "Մերժել տարանցիկ թունելներ"}, {"Decline transit tunnels", "Մերժել տարանցիկ թունելներ"},
{"Accept transit tunnels", "Ընդունել տարանցիկ թունելներ"}, {"Accept transit tunnels", "Ընդունել տարանցիկ թունելներ"},
{"Cancel graceful shutdown", "Չեղարկել սահուն անջատումը"}, {"Cancel graceful shutdown", "Չեղարկել սահուն անջատումը"},
{"Start graceful shutdown", "Սկսել սահուն անջատումը"}, {"Start graceful shutdown", "Սկսել սահուն անջատումը"},
{"Force shutdown", "Հարկադիր անջատում"}, {"Force shutdown", "Հարկադիր անջատում"},
{"Reload external CSS styles", "Վերաբեռնեք CSS ոճաթերթը"}, {"Reload external CSS styles", "Վերաբեռնեք CSS ոճաթերթը"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b> Նշում․ </b> այստեղ կատարված ցանկացած գործողություն մշտական ​​չէ և չի փոխում ձեր կազմաձևման ֆայլերը։"}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b> Նշում․ </b> այստեղ կատարված ցանկացած գործողություն մշտական ​​չէ և չի փոխում ձեր կազմաձևման ֆայլերը։"},
{"Logging level", "Գրառման աստիճանը"}, {"Logging level", "Գրառման աստիճանը"},
{"Transit tunnels limit", "Տարանցիկ թունելների սահմանափակում"}, {"Transit tunnels limit", "Տարանցիկ թունելների սահմանափակում"},
{"Change", "Փոփոխել"}, {"Change", "Փոփոխել"},
{"Change language", "Փոփոխել լեզուն"}, {"Change language", "Փոփոխել լեզուն"},
{"no transit tunnels currently built", "ընթացիկ կառուցված տարանցիկ թունելներ գոյություն չունեն"}, {"no transit tunnels currently built", "ընթացիկ կառուցված տարանցիկ թունելներ գոյություն չունեն"},
{"SAM disabled", "SAM-ն անջատված է"}, {"SAM disabled", "SAM-ն անջատված է"},
{"no sessions currently running", "ներկայումս գործող նստաշրջաններ գոյություն չունեն"}, {"no sessions currently running", "ներկայումս գործող նստաշրջաններ գոյություն չունեն"},
{"SAM session not found", "SAM նստաշրջան գոյություն չունի"}, {"SAM session not found", "SAM նստաշրջան գոյություն չունի"},
{"SAM Session", "SAM նստաշրջան"}, {"SAM Session", "SAM նստաշրջան"},
{"Server Tunnels", "Սերվերային թունելներ"}, {"Server Tunnels", "Սերվերային թունելներ"},
{"Client Forwards", "Օգտատիրական փոխանցումներ"}, {"Client Forwards", "Օգտատիրական փոխանցումներ"},
{"Server Forwards", "Սերվերային փոխանցումներ"}, {"Server Forwards", "Սերվերային փոխանցումներ"},
{"Unknown page", "Անհայտ էջ"}, {"Unknown page", "Անհայտ էջ"},
{"Invalid token", "Սխալ տոկեն"}, {"Invalid token", "Սխալ տոկեն"},
{"SUCCESS", "ՀԱՋՈՂՎԱԾ"}, {"SUCCESS", "ՀԱՋՈՂՎԱԾ"},
{"Stream closed", "Հոսքն անջատված է"}, {"Stream closed", "Հոսքն անջատված է"},
{"Stream not found or already was closed", "Հոսքը գոյություն չունի կամ արդեն ավարտված է"}, {"Stream not found or already was closed", "Հոսքը գոյություն չունի կամ արդեն ավարտված է"},
{"Destination not found", "Հասցեի վայրը չի գտնվել"}, {"Destination not found", "Հասցեի վայրը չի գտնվել"},
{"StreamID can't be null", "StreamID-ն չի կարող լինել դատարկ"}, {"StreamID can't be null", "StreamID-ն չի կարող լինել դատարկ"},
{"Return to destination page", "Վերադառնալ նախորդ էջի հասցե"}, {"Return to destination page", "Վերադառնալ նախորդ էջի հասցե"},
{"You will be redirected in 5 seconds", "Դուք կտեղափոխվեք 5 վայրկյանից"}, {"You will be redirected in 5 seconds", "Դուք կտեղափոխվեք 5 վայրկյանից"},
{"Transit tunnels count must not exceed 65535", "Տարանցիկ թունելների քանակը չպետք է գերազանցի 65535-ը"}, {"Transit tunnels count must not exceed 65535", "Տարանցիկ թունելների քանակը չպետք է գերազանցի 65535-ը"},
{"Back to commands list", "Վերադառնալ հրահանգների ցուցակ"}, {"Back to commands list", "Վերադառնալ հրահանգների ցուցակ"},
{"Register at reg.i2p", "Գրանցել reg.i2p-ում"}, {"Register at reg.i2p", "Գրանցել reg.i2p-ում"},
{"Description", "Նկարագրություն"}, {"Description", "Նկարագրություն"},
{"A bit information about service on domain", "Մի փոքր տեղեկատվություն տիրոիյթում գտնվող ծառայության մասին"}, {"A bit information about service on domain", "Մի փոքր տեղեկատվություն տիրոիյթում գտնվող ծառայության մասին"},
{"Submit", "Ուղարկվել"}, {"Submit", "Ուղարկվել"},
{"Domain can't end with .b32.i2p", "Տիրույթը չպետք է վերջանա .b32.i2p-ով"}, {"Domain can't end with .b32.i2p", "Տիրույթը չպետք է վերջանա .b32.i2p-ով"},
{"Domain must end with .i2p", "Տիրույթը պետք է վերջանա .i2p-ով"}, {"Domain must end with .i2p", "Տիրույթը պետք է վերջանա .i2p-ով"},
{"Such destination is not found", "Այդիպսի հասցե գոյություն չունի"}, {"Such destination is not found", "Այդիպսի հասցե գոյություն չունի"},
{"Unknown command", "Անհայտ հրահանգ"}, {"Unknown command", "Անհայտ հրահանգ"},
{"Command accepted", "Հրարահանգն ընդունված է"}, {"Command accepted", "Հրարահանգն ընդունված է"},
{"Proxy error", "Պրոկսի սխալ"}, {"Proxy error", "Պրոկսի սխալ"},
{"Proxy info", "Պրոկսի տեղեկություն"}, {"Proxy info", "Պրոկսի տեղեկություն"},
{"Proxy error: Host not found", "Պրոկսի սխալ՝ նման հոսթ գոյություն չունի"}, {"Proxy error: Host not found", "Պրոկսի սխալ՝ նման հոսթ գոյություն չունի"},
{"Remote host not found in router's addressbook", "Դեպի հոսթ կատարված հարցումը գոյություն չունի երթուղիչի հասցեագրքում"}, {"Remote host not found in router's addressbook", "Դեպի հոսթ կատարված հարցումը գոյություն չունի երթուղիչի հասցեագրքում"},
{"You may try to find this host on jump services below", "Ստորև Դուք կարող եք գտնել այս հոսթը jump ծառայությունների միջոցով"}, {"You may try to find this host on jump services below", "Ստորև Դուք կարող եք գտնել այս հոսթը jump ծառայությունների միջոցով"},
{"Invalid request", "Սխալ հարցում"}, {"Invalid request", "Սխալ հարցում"},
{"Proxy unable to parse your request", "Պրոկսին չի կարող հասկանալ Ձեր հարցումը"}, {"Proxy unable to parse your request", "Պրոկսին չի կարող հասկանալ Ձեր հարցումը"},
{"addresshelper is not supported", "addresshelper-ը համատեղելի չէ"}, {"addresshelper is not supported", "addresshelper-ը համատեղելի չէ"},
{"Host", "Հոսթ"}, {"Host", "Հոսթ"},
{"added to router's addressbook from helper", "Ավելացված է երթուղիչի հասցեագրքում helper-ի միջոցով"}, {"added to router's addressbook from helper", "Ավելացված է երթուղիչի հասցեագրքում helper-ի միջոցով"},
{"Click here to proceed:", "Շարունակելու համար սեղմեք այստեղ"}, {"Click here to proceed:", "Շարունակելու համար սեղմեք այստեղ"},
{"Continue", "Շարունակել"}, {"Continue", "Շարունակել"},
{"Addresshelper found", "addresshelper-ը գնտված է"}, {"Addresshelper found", "addresshelper-ը գնտված է"},
{"already in router's addressbook", "արդեն առկա է երթուղիչի հասցեագրքում"}, {"already in router's addressbook", "արդեն առկա է երթուղիչի հասցեագրքում"},
{"Click here to update record:", "Սեղմեկ այստեղ որպեսզի թարվացնեք գրառումը"}, {"Click here to update record:", "Սեղմեկ այստեղ որպեսզի թարվացնեք գրառումը"},
{"invalid request uri", "Սխալ ձևավորված URI հարցում"}, {"invalid request uri", "Սխալ ձևավորված URI հարցում"},
{"Can't detect destination host from request", "Չհաջողվեց հայնտաբերեկ վայրի հասցեն նշված հարցմամբ"}, {"Can't detect destination host from request", "Չհաջողվեց հայնտաբերեկ վայրի հասցեն նշված հարցմամբ"},
{"Outproxy failure", "Սխալ արտաքին պրոքսի"}, {"Outproxy failure", "Սխալ արտաքին պրոքսի"},
{"bad outproxy settings", "Սխալ արտաքին պրոկսի կարգավորումներ"}, {"bad outproxy settings", "Սխալ արտաքին պրոկսի կարգավորումներ"},
{"not inside I2P network, but outproxy is not enabled", "Հարցումը I2P ցանցից դուրս է, բայց արտաքին պրոքսին միացված չէ"}, {"not inside I2P network, but outproxy is not enabled", "Հարցումը I2P ցանցից դուրս է, բայց արտաքին պրոքսին միացված չէ"},
{"unknown outproxy url", "արտաքին պրոքսիի անհայտ URL"}, {"unknown outproxy url", "արտաքին պրոքսիի անհայտ URL"},
{"cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"}, {"cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"},
{"hostname too long", "Հոսթի անունը չափազանց երկար է"}, {"hostname too long", "Հոսթի անունը չափազանց երկար է"},
{"cannot connect to upstream socks proxy", "չհաջողվեց միանալ վերադաս socks պրոկսիին"}, {"cannot connect to upstream socks proxy", "չհաջողվեց միանալ վերադաս socks պրոկսիին"},
{"Cannot negotiate with socks proxy", "Չհաջողվեց պայմանավորվել վերադաս socks պրոկսիի հետ"}, {"Cannot negotiate with socks proxy", "Չհաջողվեց պայմանավորվել վերադաս socks պրոկսիի հետ"},
{"CONNECT error", "Սխալ CONNECT հարցում"}, {"CONNECT error", "Սխալ CONNECT հարցում"},
{"Failed to Connect", "Միանալ չhաջողվեց"}, {"Failed to Connect", "Միանալ չhաջողվեց"},
{"socks proxy error", "Սխալ SOCKS պրոկսի"}, {"socks proxy error", "Սխալ SOCKS պրոկսի"},
{"failed to send request to upstream", "Չհաջողվեց հարցումն ուղարկել վերադաս պրոկսիին"}, {"failed to send request to upstream", "Չհաջողվեց հարցումն ուղարկել վերադաս պրոկսիին"},
{"No Reply From socks proxy", "Բացակայում է պատասխանը SOCKS պրոկսի սերվերի կողմից"}, {"No Reply From socks proxy", "Բացակայում է պատասխանը SOCKS պրոկսի սերվերի կողմից"},
{"cannot connect", "Հնարավոր չե միանալ"}, {"cannot connect", "Հնարավոր չե միանալ"},
{"http out proxy not implemented", "Արտաքին http պրոկսին դեռ իրականացված չէ"}, {"http out proxy not implemented", "Արտաքին http պրոկսին դեռ իրականացված չէ"},
{"cannot connect to upstream http proxy", "Չհաջողվեց միանալ վերադաս http պրոկսի սերվերին"}, {"cannot connect to upstream http proxy", "Չհաջողվեց միանալ վերադաս http պրոկսի սերվերին"},
{"Host is down", "Հոսթն անհասանելի է"}, {"Host is down", "Հոսթն անհասանելի է"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ"}, {"Can't create connection to requested host, it may be down. Please try again later.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ"},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"օր", "օր"}}, {"days", {"օր", "օր"}},
{"hours", {"ժամ", "ժամ"}}, {"hours", {"ժամ", "ժամ"}},
{"minutes", {"րոպե", "րոպե"}}, {"minutes", {"րոպե", "րոպե"}},
{"seconds", {"վարկյան", "վարկյան"}}, {"seconds", {"վարկյան", "վարկյան"}},
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -15,203 +15,201 @@
// Simplified Chinese localization file // Simplified Chinese localization file
// This is an example translation file without strings in it. // This is an example translation file without strings in it.
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace chinese // language namespace
{ {
namespace chinese // language namespace // language name in lowercase
{ static std::string language = "chinese";
// language name in lowercase
static std::string language = "chinese";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return 0; return 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"KiB", "KiB"},
{"MiB", "MiB"}, {"MiB", "MiB"},
{"GiB", "GiB"}, {"GiB", "GiB"},
{"building", "正在构建"}, {"building", "正在构建"},
{"failed", "连接失败"}, {"failed", "连接失败"},
{"expiring", "即将过期"}, {"expiring", "即将过期"},
{"established", "连接已建立"}, {"established", "连接已建立"},
{"unknown", "未知"}, {"unknown", "未知"},
{"exploratory", "探测"}, {"exploratory", "探测"},
{"Purple I2P Webconsole", "Purple I2P 网页控制台"}, {"Purple I2P Webconsole", "Purple I2P 网页控制台"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> 网页控制台"}, {"<b>i2pd</b> webconsole", "<b>i2pd</b> 网页控制台"},
{"Main page", "主页"}, {"Main page", "主页"},
{"Router commands", "路由命令"}, {"Router commands", "路由命令"},
{"Local Destinations", "本地目标"}, {"Local Destinations", "本地目标"},
{"LeaseSets", "租契集"}, {"LeaseSets", "租契集"},
{"Tunnels", "隧道"}, {"Tunnels", "隧道"},
{"Transit Tunnels", "中转隧道"}, {"Transit Tunnels", "中转隧道"},
{"Transports", "传输"}, {"Transports", "传输"},
{"I2P tunnels", "I2P 隧道"}, {"I2P tunnels", "I2P 隧道"},
{"SAM sessions", "SAM 会话"}, {"SAM sessions", "SAM 会话"},
{"ERROR", "错误"}, {"ERROR", "错误"},
{"OK", "良好"}, {"OK", "良好"},
{"Testing", "测试中"}, {"Testing", "测试中"},
{"Firewalled", "受到防火墙限制"}, {"Firewalled", "受到防火墙限制"},
{"Unknown", "未知"}, {"Unknown", "未知"},
{"Proxy", "代理"}, {"Proxy", "代理"},
{"Mesh", "Mesh组网"}, {"Mesh", "Mesh组网"},
{"Error", "错误"}, {"Error", "错误"},
{"Clock skew", "时钟偏移"}, {"Clock skew", "时钟偏移"},
{"Offline", "离线"}, {"Offline", "离线"},
{"Symmetric NAT", "对称 NAT"}, {"Symmetric NAT", "对称 NAT"},
{"Uptime", "运行时间"}, {"Uptime", "运行时间"},
{"Network status", "IPv4 网络状态"}, {"Network status", "IPv4 网络状态"},
{"Network status v6", "IPv6 网络状态"}, {"Network status v6", "IPv6 网络状态"},
{"Stopping in", "距停止还有:"}, {"Stopping in", "距停止还有:"},
{"Family", "家族"}, {"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"}, {"Tunnel creation success rate", "隧道创建成功率"},
{"Received", "已接收"}, {"Received", "已接收"},
{"KiB/s", "KiB/s"}, {"KiB/s", "KiB/s"},
{"Sent", "已发送"}, {"Sent", "已发送"},
{"Transit", "中转"}, {"Transit", "中转"},
{"Data path", "数据文件路径"}, {"Data path", "数据文件路径"},
{"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"}, {"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"},
{"Router Ident", "路由身份"}, {"Router Ident", "路由身份"},
{"Router Family", "路由器家族"}, {"Router Family", "路由器家族"},
{"Router Caps", "路由器类型"}, {"Router Caps", "路由器类型"},
{"Version", "版本"}, {"Version", "版本"},
{"Our external address", "外部地址"}, {"Our external address", "外部地址"},
{"supported", "支持"}, {"supported", "支持"},
{"Routers", "路由节点"}, {"Routers", "路由节点"},
{"Floodfills", "洪泛节点"}, {"Floodfills", "洪泛节点"},
{"Client Tunnels", "客户端隧道"}, {"Client Tunnels", "客户端隧道"},
{"Services", "服务"}, {"Services", "服务"},
{"Enabled", "启用"}, {"Enabled", "启用"},
{"Disabled", "禁用"}, {"Disabled", "禁用"},
{"Encrypted B33 address", "加密的 B33 地址"}, {"Encrypted B33 address", "加密的 B33 地址"},
{"Address registration line", "地址域名注册"}, {"Address registration line", "地址域名注册"},
{"Domain", "域名"}, {"Domain", "域名"},
{"Generate", "生成"}, {"Generate", "生成"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>注意:</b> 结果字符串只能用于注册次级域名(例如example.i2p)。若需注册子域名,请使用 i2pd-tools。"}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>注意:</b> 结果字符串只能用于注册次级域名(例如example.i2p)。若需注册子域名,请使用 i2pd-tools。"},
{"Address", "地址"}, {"Address", "地址"},
{"Type", "类型"}, {"Type", "类型"},
{"EncType", "加密类型"}, {"EncType", "加密类型"},
{"Inbound tunnels", "入站隧道"}, {"Inbound tunnels", "入站隧道"},
{"ms", "毫秒"}, {"ms", "毫秒"},
{"Outbound tunnels", "出站隧道"}, {"Outbound tunnels", "出站隧道"},
{"Tags", "标签"}, {"Tags", "标签"},
{"Incoming", "传入"}, {"Incoming", "传入"},
{"Outgoing", "传出"}, {"Outgoing", "传出"},
{"Destination", "目标"}, {"Destination", "目标"},
{"Amount", "数量"}, {"Amount", "数量"},
{"Incoming Tags", "传入标签"}, {"Incoming Tags", "传入标签"},
{"Tags sessions", "标签会话"}, {"Tags sessions", "标签会话"},
{"Status", "状态"}, {"Status", "状态"},
{"Local Destination", "本地目标"}, {"Local Destination", "本地目标"},
{"Streams", ""}, {"Streams", ""},
{"Close stream", "断开流"}, {"Close stream", "断开流"},
{"I2CP session not found", "未找到 I2CP 会话"}, {"I2CP session not found", "未找到 I2CP 会话"},
{"I2CP is not enabled", "I2CP 未启用"}, {"I2CP is not enabled", "I2CP 未启用"},
{"Invalid", "无效"}, {"Invalid", "无效"},
{"Store type", "存储类型"}, {"Store type", "存储类型"},
{"Expires", "过期时间"}, {"Expires", "过期时间"},
{"Non Expired Leases", "未到期的租约"}, {"Non Expired Leases", "未到期的租约"},
{"Gateway", "网关"}, {"Gateway", "网关"},
{"TunnelID", "隧道 ID"}, {"TunnelID", "隧道 ID"},
{"EndDate", "结束日期"}, {"EndDate", "结束日期"},
{"not floodfill", "非洪泛"}, {"not floodfill", "非洪泛"},
{"Queue size", "队列大小"}, {"Queue size", "队列大小"},
{"Run peer test", "运行节点测试"}, {"Run peer test", "运行节点测试"},
{"Decline transit tunnels", "拒绝中转隧道"}, {"Decline transit tunnels", "拒绝中转隧道"},
{"Accept transit tunnels", "允许中转隧道"}, {"Accept transit tunnels", "允许中转隧道"},
{"Cancel graceful shutdown", "取消优雅地离线"}, {"Cancel graceful shutdown", "取消优雅地离线"},
{"Start graceful shutdown", "优雅地离线"}, {"Start graceful shutdown", "优雅地离线"},
{"Force shutdown", "强制停止"}, {"Force shutdown", "强制停止"},
{"Reload external CSS styles", "重载外部 CSS 样式"}, {"Reload external CSS styles", "重载外部 CSS 样式"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>注意:</b> 此处完成的任何操作都不是永久的,不会更改您的配置文件。"}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>注意:</b> 此处完成的任何操作都不是永久的,不会更改您的配置文件。"},
{"Logging level", "日志记录级别"}, {"Logging level", "日志记录级别"},
{"Transit tunnels limit", "中转隧道限制"}, {"Transit tunnels limit", "中转隧道限制"},
{"Change", "更改"}, {"Change", "更改"},
{"Change language", "更改语言"}, {"Change language", "更改语言"},
{"no transit tunnels currently built", "目前未构建中转隧道"}, {"no transit tunnels currently built", "目前未构建中转隧道"},
{"SAM disabled", "SAM 已禁用"}, {"SAM disabled", "SAM 已禁用"},
{"no sessions currently running", "没有正在运行的会话"}, {"no sessions currently running", "没有正在运行的会话"},
{"SAM session not found", "未找到 SAM 会话"}, {"SAM session not found", "未找到 SAM 会话"},
{"SAM Session", "SAM 会话"}, {"SAM Session", "SAM 会话"},
{"Server Tunnels", "服务器隧道"}, {"Server Tunnels", "服务器隧道"},
{"Client Forwards", "客户端转发"}, {"Client Forwards", "客户端转发"},
{"Server Forwards", "服务器转发"}, {"Server Forwards", "服务器转发"},
{"Unknown page", "未知页面"}, {"Unknown page", "未知页面"},
{"Invalid token", "无效令牌"}, {"Invalid token", "无效令牌"},
{"SUCCESS", "成功"}, {"SUCCESS", "成功"},
{"Stream closed", "流已关闭"}, {"Stream closed", "流已关闭"},
{"Stream not found or already was closed", "流未找到或已关闭"}, {"Stream not found or already was closed", "流未找到或已关闭"},
{"Destination not found", "找不到目标"}, {"Destination not found", "找不到目标"},
{"StreamID can't be null", "StreamID 不能为空"}, {"StreamID can't be null", "StreamID 不能为空"},
{"Return to destination page", "返回目标页面"}, {"Return to destination page", "返回目标页面"},
{"You will be redirected in 5 seconds", "您将在5秒内被重定向"}, {"You will be redirected in 5 seconds", "您将在5秒内被重定向"},
{"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"}, {"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"},
{"Back to commands list", "返回命令列表"}, {"Back to commands list", "返回命令列表"},
{"Register at reg.i2p", "在 reg.i2p 注册域名"}, {"Register at reg.i2p", "在 reg.i2p 注册域名"},
{"Description", "描述"}, {"Description", "描述"},
{"A bit information about service on domain", "在此域名上运行的服务的一些信息"}, {"A bit information about service on domain", "在此域名上运行的服务的一些信息"},
{"Submit", "提交"}, {"Submit", "提交"},
{"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"}, {"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"},
{"Domain must end with .i2p", "域名必须以 .i2p 结尾"}, {"Domain must end with .i2p", "域名必须以 .i2p 结尾"},
{"Such destination is not found", "找不到此目标"}, {"Such destination is not found", "找不到此目标"},
{"Unknown command", "未知指令"}, {"Unknown command", "未知指令"},
{"Command accepted", "已接受指令"}, {"Command accepted", "已接受指令"},
{"Proxy error", "代理错误"}, {"Proxy error", "代理错误"},
{"Proxy info", "代理信息"}, {"Proxy info", "代理信息"},
{"Proxy error: Host not found", "代理错误:未找到主机"}, {"Proxy error: Host not found", "代理错误:未找到主机"},
{"Remote host not found in router's addressbook", "在路由地址簿中未找到远程主机"}, {"Remote host not found in router's addressbook", "在路由地址簿中未找到远程主机"},
{"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务中找到该主机"}, {"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务中找到该主机"},
{"Invalid request", "无效请求"}, {"Invalid request", "无效请求"},
{"Proxy unable to parse your request", "代理无法解析您的请求"}, {"Proxy unable to parse your request", "代理无法解析您的请求"},
{"addresshelper is not supported", "不支持地址助手"}, {"addresshelper is not supported", "不支持地址助手"},
{"Host", "主机"}, {"Host", "主机"},
{"added to router's addressbook from helper", "将此地址从地址助手添加到路由地址簿"}, {"added to router's addressbook from helper", "将此地址从地址助手添加到路由地址簿"},
{"Click here to proceed:", "点击此处继续:"}, {"Click here to proceed:", "点击此处继续:"},
{"Continue", "继续"}, {"Continue", "继续"},
{"Addresshelper found", "已找到地址助手"}, {"Addresshelper found", "已找到地址助手"},
{"already in router's addressbook", "已在路由地址簿中"}, {"already in router's addressbook", "已在路由地址簿中"},
{"Click here to update record:", "点击此处更新地址簿记录"}, {"Click here to update record:", "点击此处更新地址簿记录"},
{"invalid request uri", "无效的 URL 请求"}, {"invalid request uri", "无效的 URL 请求"},
{"Can't detect destination host from request", "无法从请求中检测到目标主机"}, {"Can't detect destination host from request", "无法从请求中检测到目标主机"},
{"Outproxy failure", "出口代理故障"}, {"Outproxy failure", "出口代理故障"},
{"bad outproxy settings", "错误的出口代理设置"}, {"bad outproxy settings", "错误的出口代理设置"},
{"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"}, {"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"},
{"unknown outproxy url", "未知的出口代理地址"}, {"unknown outproxy url", "未知的出口代理地址"},
{"cannot resolve upstream proxy", "无法解析上游代理"}, {"cannot resolve upstream proxy", "无法解析上游代理"},
{"hostname too long", "主机名过长"}, {"hostname too long", "主机名过长"},
{"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"}, {"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"},
{"Cannot negotiate with socks proxy", "无法与 socks 代理协商"}, {"Cannot negotiate with socks proxy", "无法与 socks 代理协商"},
{"CONNECT error", "连接错误"}, {"CONNECT error", "连接错误"},
{"Failed to Connect", "连接失败"}, {"Failed to Connect", "连接失败"},
{"socks proxy error", "socks 代理错误"}, {"socks proxy error", "socks 代理错误"},
{"failed to send request to upstream", "向上游发送请求失败"}, {"failed to send request to upstream", "向上游发送请求失败"},
{"No Reply From socks proxy", "没有来自 socks 代理的回复"}, {"No Reply From socks proxy", "没有来自 socks 代理的回复"},
{"cannot connect", "无法连接"}, {"cannot connect", "无法连接"},
{"http out proxy not implemented", "http 出口代理未实现"}, {"http out proxy not implemented", "http 出口代理未实现"},
{"cannot connect to upstream http proxy", "无法连接到上游 http 代理"}, {"cannot connect to upstream http proxy", "无法连接到上游 http 代理"},
{"Host is down", "主机已关闭"}, {"Host is down", "主机已关闭"},
{"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"}, {"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {""}}, {"days", {""}},
{"hours", {""}}, {"hours", {""}},
{"minutes", {""}}, {"minutes", {""}},
{"seconds", {""}}, {"seconds", {""}},
{"", {""}}, {"", {""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -15,36 +15,34 @@
// English localization file // English localization file
// This is an example translation file without strings in it. // This is an example translation file without strings in it.
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace english // language namespace
{ {
namespace english // language namespace // language name in lowercase
{ static std::string language = "english";
// language name in lowercase
static std::string language = "english";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -14,198 +14,196 @@
// French localization file // French localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace french // language namespace
{ {
namespace french // language namespace // language name in lowercase
{ static std::string language = "french";
// language name in lowercase
static std::string language = "french";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "Kio"}, {"KiB", "Kio"},
{"MiB", "Mio"}, {"MiB", "Mio"},
{"GiB", "Gio"}, {"GiB", "Gio"},
{"building", "En construction"}, {"building", "En construction"},
{"failed", "échoué"}, {"failed", "échoué"},
{"expiring", "expiré"}, {"expiring", "expiré"},
{"established", "établi"}, {"established", "établi"},
{"unknown", "inconnu"}, {"unknown", "inconnu"},
{"exploratory", "exploratoire"}, {"exploratory", "exploratoire"},
{"Purple I2P Webconsole", "Console web Purple I2P"}, {"Purple I2P Webconsole", "Console web Purple I2P"},
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"}, {"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
{"Main page", "Page principale"}, {"Main page", "Page principale"},
{"Router commands", "Commandes du routeur"}, {"Router commands", "Commandes du routeur"},
{"Local Destinations", "Destinations locales"}, {"Local Destinations", "Destinations locales"},
{"LeaseSets", "Jeu de baux"}, {"LeaseSets", "Jeu de baux"},
{"Tunnels", "Tunnels"}, {"Tunnels", "Tunnels"},
{"Transit Tunnels", "Tunnels transitoires"}, {"Transit Tunnels", "Tunnels transitoires"},
{"Transports", "Transports"}, {"Transports", "Transports"},
{"I2P tunnels", "Tunnels I2P"}, {"I2P tunnels", "Tunnels I2P"},
{"SAM sessions", "Sessions SAM"}, {"SAM sessions", "Sessions SAM"},
{"ERROR", "ERREUR"}, {"ERROR", "ERREUR"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Test en cours"}, {"Testing", "Test en cours"},
{"Firewalled", "Derrière un pare-feu"}, {"Firewalled", "Derrière un pare-feu"},
{"Unknown", "Inconnu"}, {"Unknown", "Inconnu"},
{"Proxy", "Proxy"}, {"Proxy", "Proxy"},
{"Mesh", "Maillé"}, {"Mesh", "Maillé"},
{"Error", "Erreur"}, {"Error", "Erreur"},
{"Clock skew", "Horloge décalée"}, {"Clock skew", "Horloge décalée"},
{"Offline", "Hors ligne"}, {"Offline", "Hors ligne"},
{"Symmetric NAT", "NAT symétrique"}, {"Symmetric NAT", "NAT symétrique"},
{"Uptime", "Temps de fonctionnement"}, {"Uptime", "Temps de fonctionnement"},
{"Network status", "État du réseau"}, {"Network status", "État du réseau"},
{"Network status v6", "État du réseau v6"}, {"Network status v6", "État du réseau v6"},
{"Stopping in", "Arrêt dans"}, {"Stopping in", "Arrêt dans"},
{"Family", "Famille"}, {"Family", "Famille"},
{"Tunnel creation success rate", "Taux de succès de création de tunnels"}, {"Tunnel creation success rate", "Taux de succès de création de tunnels"},
{"Received", "Reçu"}, {"Received", "Reçu"},
{"KiB/s", "kio/s"}, {"KiB/s", "kio/s"},
{"Sent", "Envoyé"}, {"Sent", "Envoyé"},
{"Transit", "Transité"}, {"Transit", "Transité"},
{"Data path", "Emplacement des données"}, {"Data path", "Emplacement des données"},
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."}, {"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."},
{"Router Ident", "Identifiant du routeur"}, {"Router Ident", "Identifiant du routeur"},
{"Router Family", "Famille du routeur"}, {"Router Family", "Famille du routeur"},
{"Router Caps", "Limiteurs du routeur"}, {"Router Caps", "Limiteurs du routeur"},
{"Version", "Version"}, {"Version", "Version"},
{"Our external address", "Notre adresse externe"}, {"Our external address", "Notre adresse externe"},
{"supported", "supporté"}, {"supported", "supporté"},
{"Routers", "Routeurs"}, {"Routers", "Routeurs"},
{"Client Tunnels", "Tunnels clients"}, {"Client Tunnels", "Tunnels clients"},
{"Services", "Services"}, {"Services", "Services"},
{"Enabled", "Activé"}, {"Enabled", "Activé"},
{"Disabled", "Désactivé"}, {"Disabled", "Désactivé"},
{"Encrypted B33 address", "Adresse B33 chiffrée"}, {"Encrypted B33 address", "Adresse B33 chiffrée"},
{"Address registration line", "Ligne d'inscription de l'adresse"}, {"Address registration line", "Ligne d'inscription de l'adresse"},
{"Domain", "Domaine"}, {"Domain", "Domaine"},
{"Generate", "Générer"}, {"Generate", "Générer"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."},
{"Address", "Adresse"}, {"Address", "Adresse"},
{"Type", "Type"}, {"Type", "Type"},
{"Inbound tunnels", "Tunnels entrants"}, {"Inbound tunnels", "Tunnels entrants"},
{"ms", "ms"}, {"ms", "ms"},
{"Outbound tunnels", "Tunnels sortants"}, {"Outbound tunnels", "Tunnels sortants"},
{"Tags", "Balises"}, {"Tags", "Balises"},
{"Incoming", "Entrant"}, {"Incoming", "Entrant"},
{"Outgoing", "Sortant"}, {"Outgoing", "Sortant"},
{"Destination", "Destination"}, {"Destination", "Destination"},
{"Amount", "Quantité"}, {"Amount", "Quantité"},
{"Incoming Tags", "Balises entrantes"}, {"Incoming Tags", "Balises entrantes"},
{"Tags sessions", "Sessions des balises"}, {"Tags sessions", "Sessions des balises"},
{"Status", "Statut"}, {"Status", "Statut"},
{"Local Destination", "Destination locale"}, {"Local Destination", "Destination locale"},
{"Streams", "Flux"}, {"Streams", "Flux"},
{"Close stream", "Fermer le flux"}, {"Close stream", "Fermer le flux"},
{"I2CP session not found", "Session I2CP introuvable"}, {"I2CP session not found", "Session I2CP introuvable"},
{"I2CP is not enabled", "I2CP est désactivé"}, {"I2CP is not enabled", "I2CP est désactivé"},
{"Invalid", "Invalide"}, {"Invalid", "Invalide"},
{"Store type", "Type de stockage"}, {"Store type", "Type de stockage"},
{"Expires", "Expire"}, {"Expires", "Expire"},
{"Non Expired Leases", "Baux non expirés"}, {"Non Expired Leases", "Baux non expirés"},
{"Gateway", "Passerelle"}, {"Gateway", "Passerelle"},
{"TunnelID", "ID du tunnel"}, {"TunnelID", "ID du tunnel"},
{"EndDate", "Date de fin"}, {"EndDate", "Date de fin"},
{"Queue size", "Longueur de la file"}, {"Queue size", "Longueur de la file"},
{"Run peer test", "Lancer test des pairs"}, {"Run peer test", "Lancer test des pairs"},
{"Decline transit tunnels", "Refuser les tunnels transitoires"}, {"Decline transit tunnels", "Refuser les tunnels transitoires"},
{"Accept transit tunnels", "Accepter les tunnels transitoires"}, {"Accept transit tunnels", "Accepter les tunnels transitoires"},
{"Cancel graceful shutdown", "Annuler l'arrêt gracieux"}, {"Cancel graceful shutdown", "Annuler l'arrêt gracieux"},
{"Start graceful shutdown", "Démarrer l'arrêt gracieux"}, {"Start graceful shutdown", "Démarrer l'arrêt gracieux"},
{"Force shutdown", "Forcer l'arrêt"}, {"Force shutdown", "Forcer l'arrêt"},
{"Reload external CSS styles", "Rafraîchir les styles CSS externes"}, {"Reload external CSS styles", "Rafraîchir les styles CSS externes"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Note:</b> Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Note:</b> Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."},
{"Logging level", "Niveau de journalisation"}, {"Logging level", "Niveau de journalisation"},
{"Transit tunnels limit", "Limite sur les tunnels transitoires"}, {"Transit tunnels limit", "Limite sur les tunnels transitoires"},
{"Change", "Changer"}, {"Change", "Changer"},
{"Change language", "Changer la langue"}, {"Change language", "Changer la langue"},
{"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"}, {"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"},
{"SAM disabled", "SAM désactivé"}, {"SAM disabled", "SAM désactivé"},
{"no sessions currently running", "aucune session présentement en cours"}, {"no sessions currently running", "aucune session présentement en cours"},
{"SAM session not found", "session SAM introuvable"}, {"SAM session not found", "session SAM introuvable"},
{"SAM Session", "Session SAM"}, {"SAM Session", "Session SAM"},
{"Server Tunnels", "Tunnels serveurs"}, {"Server Tunnels", "Tunnels serveurs"},
{"Unknown page", "Page inconnue"}, {"Unknown page", "Page inconnue"},
{"Invalid token", "Jeton invalide"}, {"Invalid token", "Jeton invalide"},
{"SUCCESS", "SUCCÈS"}, {"SUCCESS", "SUCCÈS"},
{"Stream closed", "Flux fermé"}, {"Stream closed", "Flux fermé"},
{"Stream not found or already was closed", "Flux introuvable ou déjà fermé"}, {"Stream not found or already was closed", "Flux introuvable ou déjà fermé"},
{"Destination not found", "Destination introuvable"}, {"Destination not found", "Destination introuvable"},
{"StreamID can't be null", "StreamID ne peut pas être vide"}, {"StreamID can't be null", "StreamID ne peut pas être vide"},
{"Return to destination page", "Retourner à la page de destination"}, {"Return to destination page", "Retourner à la page de destination"},
{"You will be redirected in 5 seconds", "Vous allez être redirigé dans cinq secondes"}, {"You will be redirected in 5 seconds", "Vous allez être redirigé dans cinq secondes"},
{"Transit tunnels count must not exceed 65535", "Le nombre de tunnels transitoires ne doit pas dépasser 65535"}, {"Transit tunnels count must not exceed 65535", "Le nombre de tunnels transitoires ne doit pas dépasser 65535"},
{"Back to commands list", "Retour à la liste des commandes"}, {"Back to commands list", "Retour à la liste des commandes"},
{"Register at reg.i2p", "Inscription à reg.i2p"}, {"Register at reg.i2p", "Inscription à reg.i2p"},
{"Description", "Description"}, {"Description", "Description"},
{"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"}, {"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"},
{"Submit", "Soumettre"}, {"Submit", "Soumettre"},
{"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"}, {"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"},
{"Domain must end with .i2p", "Le domaine doit terminer par .i2p"}, {"Domain must end with .i2p", "Le domaine doit terminer par .i2p"},
{"Such destination is not found", "Cette destination est introuvable"}, {"Such destination is not found", "Cette destination est introuvable"},
{"Unknown command", "Commande inconnue"}, {"Unknown command", "Commande inconnue"},
{"Command accepted", "Commande acceptée"}, {"Command accepted", "Commande acceptée"},
{"Proxy error", "Erreur de proxy"}, {"Proxy error", "Erreur de proxy"},
{"Proxy info", "Information sur le proxy"}, {"Proxy info", "Information sur le proxy"},
{"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"}, {"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"},
{"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"}, {"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"},
{"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"}, {"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"},
{"Invalid request", "Requête invalide"}, {"Invalid request", "Requête invalide"},
{"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"}, {"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"},
{"addresshelper is not supported", "Assistant d'adresse non supporté"}, {"addresshelper is not supported", "Assistant d'adresse non supporté"},
{"Host", "Hôte"}, {"Host", "Hôte"},
{"added to router's addressbook from helper", "Ajouté au carnet d'adresse du routeur par l'assistant"}, {"added to router's addressbook from helper", "Ajouté au carnet d'adresse du routeur par l'assistant"},
{"Click here to proceed:", "Cliquez ici pour continuer:"}, {"Click here to proceed:", "Cliquez ici pour continuer:"},
{"Continue", "Continuer"}, {"Continue", "Continuer"},
{"Addresshelper found", "Assistant d'adresse trouvé"}, {"Addresshelper found", "Assistant d'adresse trouvé"},
{"already in router's addressbook", "déjà dans le carnet d'adresses du routeur"}, {"already in router's addressbook", "déjà dans le carnet d'adresses du routeur"},
{"Click here to update record:", "Cliquez ici pour mettre à jour le carnet d'adresse:"}, {"Click here to update record:", "Cliquez ici pour mettre à jour le carnet d'adresse:"},
{"invalid request uri", "uri de la requête invalide"}, {"invalid request uri", "uri de la requête invalide"},
{"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"}, {"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"},
{"Outproxy failure", "Échec de proxy de sortie"}, {"Outproxy failure", "Échec de proxy de sortie"},
{"bad outproxy settings", "Mauvaise configuration du proxy de sortie"}, {"bad outproxy settings", "Mauvaise configuration du proxy de sortie"},
{"not inside I2P network, but outproxy is not enabled", "pas dans le réseau I2P, mais le proxy de sortie n'est pas activé"}, {"not inside I2P network, but outproxy is not enabled", "pas dans le réseau I2P, mais le proxy de sortie n'est pas activé"},
{"unknown outproxy url", "URL du proxy de sortie inconnu"}, {"unknown outproxy url", "URL du proxy de sortie inconnu"},
{"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"}, {"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"},
{"hostname too long", "nom d'hôte trop long"}, {"hostname too long", "nom d'hôte trop long"},
{"cannot connect to upstream socks proxy", "impossible de se connecter au proxy socks en amont"}, {"cannot connect to upstream socks proxy", "impossible de se connecter au proxy socks en amont"},
{"Cannot negotiate with socks proxy", "Impossible de négocier avec le proxy socks"}, {"Cannot negotiate with socks proxy", "Impossible de négocier avec le proxy socks"},
{"CONNECT error", "Erreur de connexion"}, {"CONNECT error", "Erreur de connexion"},
{"Failed to Connect", "Échec de connexion"}, {"Failed to Connect", "Échec de connexion"},
{"socks proxy error", "Erreur de proxy socks"}, {"socks proxy error", "Erreur de proxy socks"},
{"failed to send request to upstream", "Erreur lors de l'envoie de la requête en amont"}, {"failed to send request to upstream", "Erreur lors de l'envoie de la requête en amont"},
{"No Reply From socks proxy", "Pas de réponse du proxy socks"}, {"No Reply From socks proxy", "Pas de réponse du proxy socks"},
{"cannot connect", "impossible de connecter"}, {"cannot connect", "impossible de connecter"},
{"http out proxy not implemented", "Proxy de sortie HTTP non implémenté"}, {"http out proxy not implemented", "Proxy de sortie HTTP non implémenté"},
{"cannot connect to upstream http proxy", "impossible de se connecter au proxy HTTP en amont"}, {"cannot connect to upstream http proxy", "impossible de se connecter au proxy HTTP en amont"},
{"Host is down", "Hôte hors service"}, {"Host is down", "Hôte hors service"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Impossible d'établir une connexion avec l'hôte, il est peut-être hors service. Veuillez réessayer plus tard."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Impossible d'établir une connexion avec l'hôte, il est peut-être hors service. Veuillez réessayer plus tard."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"jour", "jours"}}, {"days", {"jour", "jours"}},
{"hours", {"heure", "heures"}}, {"hours", {"heure", "heures"}},
{"minutes", {"minute", "minutes"}}, {"minutes", {"minute", "minutes"}},
{"seconds", {"seconde", "secondes"}}, {"seconds", {"seconde", "secondes"}},
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -14,203 +14,201 @@
// German localization file // German localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace german // language namespace
{ {
namespace german // language namespace // language name in lowercase
{ static std::string language = "german";
// language name in lowercase
static std::string language = "german";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"Purple I2P Webconsole", "Purple-I2P-Webkonsole"}, {"Purple I2P Webconsole", "Purple-I2P-Webkonsole"},
{"KiB", "KiB"}, {"KiB", "KiB"},
{"MiB", "MiB"}, {"MiB", "MiB"},
{"GiB", "GiB"}, {"GiB", "GiB"},
{"building", "In Bau"}, {"building", "In Bau"},
{"failed", "fehlgeschlagen"}, {"failed", "fehlgeschlagen"},
{"expiring", "läuft ab"}, {"expiring", "läuft ab"},
{"established", "hergestellt"}, {"established", "hergestellt"},
{"unknown", "Unbekannt"}, {"unknown", "Unbekannt"},
{"exploratory", "erforschend"}, {"exploratory", "erforschend"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"}, {"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"},
{"Main page", "Startseite"}, {"Main page", "Startseite"},
{"Router commands", "Routerbefehle"}, {"Router commands", "Routerbefehle"},
{"Local Destinations", "Lokale Ziele"}, {"Local Destinations", "Lokale Ziele"},
{"LeaseSets", "LeaseSets"}, {"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnel"}, {"Tunnels", "Tunnel"},
{"Transit Tunnels", "Transittunnel"}, {"Transit Tunnels", "Transittunnel"},
{"Transports", "Transporte"}, {"Transports", "Transporte"},
{"I2P tunnels", "I2P-Tunnel"}, {"I2P tunnels", "I2P-Tunnel"},
{"SAM sessions", "SAM-Sitzungen"}, {"SAM sessions", "SAM-Sitzungen"},
{"ERROR", "FEHLER"}, {"ERROR", "FEHLER"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Testen"}, {"Testing", "Testen"},
{"Firewalled", "Hinter einer Firewall"}, {"Firewalled", "Hinter einer Firewall"},
{"Unknown", "Unbekannt"}, {"Unknown", "Unbekannt"},
{"Proxy", "Proxy"}, {"Proxy", "Proxy"},
{"Mesh", "Mesh"}, {"Mesh", "Mesh"},
{"Error", "Fehler"}, {"Error", "Fehler"},
{"Clock skew", "Zeitabweichung"}, {"Clock skew", "Zeitabweichung"},
{"Offline", "Offline"}, {"Offline", "Offline"},
{"Symmetric NAT", "Symmetrisches NAT"}, {"Symmetric NAT", "Symmetrisches NAT"},
{"Uptime", "Laufzeit"}, {"Uptime", "Laufzeit"},
{"Network status", "Netzwerkstatus"}, {"Network status", "Netzwerkstatus"},
{"Network status v6", "Netzwerkstatus v6"}, {"Network status v6", "Netzwerkstatus v6"},
{"Stopping in", "Stoppt in"}, {"Stopping in", "Stoppt in"},
{"Family", "Familie"}, {"Family", "Familie"},
{"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"}, {"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"},
{"Received", "Eingegangen"}, {"Received", "Eingegangen"},
{"KiB/s", "KiB/s"}, {"KiB/s", "KiB/s"},
{"Sent", "Gesendet"}, {"Sent", "Gesendet"},
{"Transit", "Transit"}, {"Transit", "Transit"},
{"Data path", "Datenpfad"}, {"Data path", "Datenpfad"},
{"Hidden content. Press on text to see.", "Versteckter Inhalt. Klicke hier, um ihn zu sehen."}, {"Hidden content. Press on text to see.", "Versteckter Inhalt. Klicke hier, um ihn zu sehen."},
{"Router Ident", "Routeridentität"}, {"Router Ident", "Routeridentität"},
{"Router Family", "Routerfamilie"}, {"Router Family", "Routerfamilie"},
{"Router Caps", "Routerattribute"}, {"Router Caps", "Routerattribute"},
{"Version", "Version"}, {"Version", "Version"},
{"Our external address", "Unsere externe Adresse"}, {"Our external address", "Unsere externe Adresse"},
{"supported", "unterstützt"}, {"supported", "unterstützt"},
{"Routers", "Router"}, {"Routers", "Router"},
{"Floodfills", "Floodfills"}, {"Floodfills", "Floodfills"},
{"Client Tunnels", "Clienttunnel"}, {"Client Tunnels", "Clienttunnel"},
{"Services", "Services"}, {"Services", "Services"},
{"Enabled", "Aktiviert"}, {"Enabled", "Aktiviert"},
{"Disabled", "Deaktiviert"}, {"Disabled", "Deaktiviert"},
{"Encrypted B33 address", "Verschlüsselte B33-Adresse"}, {"Encrypted B33 address", "Verschlüsselte B33-Adresse"},
{"Address registration line", "Adressregistrierungszeile"}, {"Address registration line", "Adressregistrierungszeile"},
{"Domain", "Domain"}, {"Domain", "Domain"},
{"Generate", "Generieren"}, {"Generate", "Generieren"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Hinweis:</b> Der resultierende String kann nur für die Registrierung einer 2LD-Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Hinweis:</b> Der resultierende String kann nur für die Registrierung einer 2LD-Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."},
{"Address", "Adresse"}, {"Address", "Adresse"},
{"Type", "Typ"}, {"Type", "Typ"},
{"EncType", "Verschlüsselungstyp"}, {"EncType", "Verschlüsselungstyp"},
{"Inbound tunnels", "Eingehende Tunnel"}, {"Inbound tunnels", "Eingehende Tunnel"},
{"ms", "ms"}, {"ms", "ms"},
{"Outbound tunnels", "Ausgehende Tunnel"}, {"Outbound tunnels", "Ausgehende Tunnel"},
{"Tags", "Tags"}, {"Tags", "Tags"},
{"Incoming", "Eingehend"}, {"Incoming", "Eingehend"},
{"Outgoing", "Ausgehend"}, {"Outgoing", "Ausgehend"},
{"Destination", "Ziel"}, {"Destination", "Ziel"},
{"Amount", "Anzahl"}, {"Amount", "Anzahl"},
{"Incoming Tags", "Eingehende Tags"}, {"Incoming Tags", "Eingehende Tags"},
{"Tags sessions", "Tags-Sitzungen"}, {"Tags sessions", "Tags-Sitzungen"},
{"Status", "Status"}, {"Status", "Status"},
{"Local Destination", "Lokales Ziel"}, {"Local Destination", "Lokales Ziel"},
{"Streams", "Streams"}, {"Streams", "Streams"},
{"Close stream", "Stream schließen"}, {"Close stream", "Stream schließen"},
{"I2CP session not found", "I2CP-Sitzung nicht gefunden"}, {"I2CP session not found", "I2CP-Sitzung nicht gefunden"},
{"I2CP is not enabled", "I2CP ist nicht aktiviert"}, {"I2CP is not enabled", "I2CP ist nicht aktiviert"},
{"Invalid", "Ungültig"}, {"Invalid", "Ungültig"},
{"Store type", "Speichertyp"}, {"Store type", "Speichertyp"},
{"Expires", "Ablaufdatum"}, {"Expires", "Ablaufdatum"},
{"Non Expired Leases", "Nicht abgelaufene Leases"}, {"Non Expired Leases", "Nicht abgelaufene Leases"},
{"Gateway", "Gateway"}, {"Gateway", "Gateway"},
{"TunnelID", "TunnelID"}, {"TunnelID", "TunnelID"},
{"EndDate", "Enddatum"}, {"EndDate", "Enddatum"},
{"not floodfill", "kein Floodfill"}, {"not floodfill", "kein Floodfill"},
{"Queue size", "Größe der Warteschlange"}, {"Queue size", "Größe der Warteschlange"},
{"Run peer test", "Peer-Test durchführen"}, {"Run peer test", "Peer-Test durchführen"},
{"Decline transit tunnels", "Transittunnel ablehnen"}, {"Decline transit tunnels", "Transittunnel ablehnen"},
{"Accept transit tunnels", "Transittunnel akzeptieren"}, {"Accept transit tunnels", "Transittunnel akzeptieren"},
{"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"}, {"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"},
{"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"}, {"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"},
{"Force shutdown", "Herunterfahren erzwingen"}, {"Force shutdown", "Herunterfahren erzwingen"},
{"Reload external CSS styles", "Lade externe CSS-Stile neu"}, {"Reload external CSS styles", "Lade externe CSS-Stile neu"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Hinweis:</b> Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Hinweis:</b> Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."},
{"Logging level", "Protokollierungslevel"}, {"Logging level", "Protokollierungslevel"},
{"Transit tunnels limit", "Limit für Transittunnel"}, {"Transit tunnels limit", "Limit für Transittunnel"},
{"Change", "Ändern"}, {"Change", "Ändern"},
{"Change language", "Sprache ändern"}, {"Change language", "Sprache ändern"},
{"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"}, {"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"},
{"SAM disabled", "SAM deaktiviert"}, {"SAM disabled", "SAM deaktiviert"},
{"no sessions currently running", "Derzeit keine laufenden Sitzungen"}, {"no sessions currently running", "Derzeit keine laufenden Sitzungen"},
{"SAM session not found", "SAM-Sitzung nicht gefunden"}, {"SAM session not found", "SAM-Sitzung nicht gefunden"},
{"SAM Session", "SAM-Sitzung"}, {"SAM Session", "SAM-Sitzung"},
{"Server Tunnels", "Servertunnel"}, {"Server Tunnels", "Servertunnel"},
{"Client Forwards", "Client-Weiterleitungen"}, {"Client Forwards", "Client-Weiterleitungen"},
{"Server Forwards", "Server-Weiterleitungen"}, {"Server Forwards", "Server-Weiterleitungen"},
{"Unknown page", "Unbekannte Seite"}, {"Unknown page", "Unbekannte Seite"},
{"Invalid token", "Ungültiger Token"}, {"Invalid token", "Ungültiger Token"},
{"SUCCESS", "ERFOLGREICH"}, {"SUCCESS", "ERFOLGREICH"},
{"Stream closed", "Stream geschlossen"}, {"Stream closed", "Stream geschlossen"},
{"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"}, {"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"},
{"Destination not found", "Ziel nicht gefunden"}, {"Destination not found", "Ziel nicht gefunden"},
{"StreamID can't be null", "StreamID kann nicht null sein"}, {"StreamID can't be null", "StreamID kann nicht null sein"},
{"Return to destination page", "Zurück zur Ziel-Seite"}, {"Return to destination page", "Zurück zur Ziel-Seite"},
{"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"}, {"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"},
{"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"}, {"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"},
{"Back to commands list", "Zurück zur Befehlsliste"}, {"Back to commands list", "Zurück zur Befehlsliste"},
{"Register at reg.i2p", "Auf reg.i2p registrieren"}, {"Register at reg.i2p", "Auf reg.i2p registrieren"},
{"Description", "Beschreibung"}, {"Description", "Beschreibung"},
{"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"}, {"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"},
{"Submit", "Absenden"}, {"Submit", "Absenden"},
{"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"}, {"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"},
{"Domain must end with .i2p", "Domain muss auf .i2p enden"}, {"Domain must end with .i2p", "Domain muss auf .i2p enden"},
{"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"}, {"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"},
{"Unknown command", "Unbekannter Befehl"}, {"Unknown command", "Unbekannter Befehl"},
{"Command accepted", "Befehl akzeptiert"}, {"Command accepted", "Befehl akzeptiert"},
{"Proxy error", "Proxy-Fehler"}, {"Proxy error", "Proxy-Fehler"},
{"Proxy info", "Proxy-Info"}, {"Proxy info", "Proxy-Info"},
{"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"}, {"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"},
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"}, {"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"},
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"}, {"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"},
{"Invalid request", "Ungültige Anfrage"}, {"Invalid request", "Ungültige Anfrage"},
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"}, {"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"},
{"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"}, {"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"},
{"Host", "Host"}, {"Host", "Host"},
{"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"}, {"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"},
{"Click here to proceed:", "Klicke hier um fortzufahren:"}, {"Click here to proceed:", "Klicke hier um fortzufahren:"},
{"Continue", "Fortsetzen"}, {"Continue", "Fortsetzen"},
{"Addresshelper found", "Adresshelfer gefunden"}, {"Addresshelper found", "Adresshelfer gefunden"},
{"already in router's addressbook", "bereits im Adressbuch des Routers"}, {"already in router's addressbook", "bereits im Adressbuch des Routers"},
{"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"}, {"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"},
{"invalid request uri", "ungültige Anfrage-URI"}, {"invalid request uri", "ungültige Anfrage-URI"},
{"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"}, {"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"},
{"Outproxy failure", "Outproxy-Fehler"}, {"Outproxy failure", "Outproxy-Fehler"},
{"bad outproxy settings", "ungültige Outproxy-Einstellungen"}, {"bad outproxy settings", "ungültige Outproxy-Einstellungen"},
{"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"}, {"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
{"unknown outproxy url", "unbekannte Outproxy-URL"}, {"unknown outproxy url", "unbekannte Outproxy-URL"},
{"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"}, {"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"},
{"hostname too long", "Hostname zu lang"}, {"hostname too long", "Hostname zu lang"},
{"cannot connect to upstream socks proxy", "Kann keine Verbindung zum Upstream-Socks-Proxy herstellen"}, {"cannot connect to upstream socks proxy", "Kann keine Verbindung zum Upstream-Socks-Proxy herstellen"},
{"Cannot negotiate with socks proxy", "Kann nicht mit Socks-Proxy verhandeln"}, {"Cannot negotiate with socks proxy", "Kann nicht mit Socks-Proxy verhandeln"},
{"CONNECT error", "CONNECT-Fehler"}, {"CONNECT error", "CONNECT-Fehler"},
{"Failed to Connect", "Verbindung konnte nicht hergestellt werden"}, {"Failed to Connect", "Verbindung konnte nicht hergestellt werden"},
{"socks proxy error", "Socks-Proxy-Fehler"}, {"socks proxy error", "Socks-Proxy-Fehler"},
{"failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"}, {"failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"},
{"No Reply From socks proxy", "Keine Antwort vom Socks-Proxy"}, {"No Reply From socks proxy", "Keine Antwort vom Socks-Proxy"},
{"cannot connect", "kann nicht verbinden"}, {"cannot connect", "kann nicht verbinden"},
{"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"}, {"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"},
{"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"}, {"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"},
{"Host is down", "Host ist offline"}, {"Host is down", "Host ist offline"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbauen, vielleicht ist er offline. Versuche es später noch mal."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbauen, vielleicht ist er offline. Versuche es später noch mal."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"Tag", "Tage"}}, {"days", {"Tag", "Tage"}},
{"hours", {"Stunde", "Stunden"}}, {"hours", {"Stunde", "Stunden"}},
{"minutes", {"Minute", "Minuten"}}, {"minutes", {"Minute", "Minuten"}},
{"seconds", {"Sekunde", "Sekunden"}}, {"seconds", {"Sekunde", "Sekunden"}},
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -11,35 +11,29 @@
#include "ClientContext.h" #include "ClientContext.h"
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n inline void SetLanguage(const std::string &lang) {
{ const auto it = i2p::i18n::languages.find(lang);
inline void SetLanguage(const std::string &lang) if (it == i2p::i18n::languages.end()) // fallback
{ i2p::client::context.SetLanguage(i2p::i18n::english::GetLocale());
const auto it = i2p::i18n::languages.find(lang); else
if (it == i2p::i18n::languages.end()) // fallback i2p::client::context.SetLanguage(it->second.LocaleFunc());
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); }
else
i2p::client::context.SetLanguage (it->second.LocaleFunc());
}
inline std::string translate (const std::string& arg) inline std::string translate(const std::string &arg) {
{ return i2p::client::context.GetLanguage()->GetString(arg);
return i2p::client::context.GetLanguage ()->GetString (arg); }
}
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n) inline std::string translate(const std::string &arg, const std::string &arg2, const int &n) {
{ return i2p::client::context.GetLanguage()->GetPlural(arg, arg2, n);
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n); }
} } // i18n
} // i18n
} // i2p } // i2p
template<typename... TArgs> template<typename... TArgs>
std::string tr (TArgs&&... args) std::string tr(TArgs &&... args) {
{ return i2p::i18n::translate(std::forward<TArgs>(args)...);
return i2p::i18n::translate(std::forward<TArgs>(args)...);
} }
#endif // __I18N_H__ #endif // __I18N_H__

View file

@ -9,97 +9,85 @@
#ifndef __I18N_LANGS_H__ #ifndef __I18N_LANGS_H__
#define __I18N_LANGS_H__ #define __I18N_LANGS_H__
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n class Locale {
{ public:
class Locale Locale(
{ const std::string &language,
public: const std::map <std::string, std::string> &strings,
Locale ( const std::map <std::string, std::vector<std::string>> &plurals,
const std::string& language, std::function<int(int)> formula
const std::map<std::string, std::string>& strings, ) : m_Language(language), m_Strings(strings), m_Plurals(plurals), m_Formula(formula) {};
const std::map<std::string, std::vector<std::string>>& plurals,
std::function<int(int)> formula
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
// Get activated language name for webconsole // Get activated language name for webconsole
std::string GetLanguage() const std::string GetLanguage() const {
{ return m_Language;
return m_Language; }
}
std::string GetString (const std::string& arg) const std::string GetString(const std::string &arg) const {
{ const auto it = m_Strings.find(arg);
const auto it = m_Strings.find(arg); if (it == m_Strings.end()) {
if (it == m_Strings.end()) return arg;
{ } else {
return arg; return it->second;
} }
else }
{
return it->second;
}
}
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const std::string GetPlural(const std::string &arg, const std::string &arg2, const int &n) const {
{ const auto it = m_Plurals.find(arg2);
const auto it = m_Plurals.find(arg2); if (it == m_Plurals.end()) // not found, fallback to english
if (it == m_Plurals.end()) // not found, fallback to english {
{ return n == 1 ? arg : arg2;
return n == 1 ? arg : arg2; } else {
} int form = m_Formula(n);
else return it->second[form];
{ }
int form = m_Formula(n); }
return it->second[form];
}
}
private: private:
const std::string m_Language; const std::string m_Language;
const std::map<std::string, std::string> m_Strings; const std::map <std::string, std::string> m_Strings;
const std::map<std::string, std::vector<std::string>> m_Plurals; const std::map <std::string, std::vector<std::string>> m_Plurals;
std::function<int(int)> m_Formula; std::function<int(int)> m_Formula;
}; };
struct langData struct langData {
{ std::string LocaleName; // localized name
std::string LocaleName; // localized name std::string ShortCode; // short language code, like "en"
std::string ShortCode; // short language code, like "en" std::function<std::shared_ptr<const i2p::i18n::Locale>(void)> LocaleFunc;
std::function<std::shared_ptr<const i2p::i18n::Locale> (void)> LocaleFunc; };
};
// Add localization here with language name as namespace // Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
/** /**
* That map contains international language name lower-case, name in it's language and it's code * That map contains international language name lower-case, name in it's language and it's code
*/ */
static std::map<std::string, langData> languages static std::map <std::string, langData> languages
{ {
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} }, {"afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale}},
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} }, {"armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale}},
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} }, {"chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale}},
{ "english", {"English", "en", i2p::i18n::english::GetLocale} }, {"english", {"English", "en", i2p::i18n::english::GetLocale}},
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} }, {"french", {"Français", "fr", i2p::i18n::french::GetLocale}},
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} }, {"german", {"Deutsch", "de", i2p::i18n::german::GetLocale}},
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} }, {"russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale}},
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, {"turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale}},
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, {"ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale}},
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} }, {"uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale}},
}; };
} // i18n } // i18n
} // i2p } // i2p
#endif // __I18N_LANGS_H__ #endif // __I18N_LANGS_H__

View file

@ -14,202 +14,201 @@
// Russian localization file // Russian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace russian // language namespace
{ {
namespace russian // language namespace // language name in lowercase
{ static std::string language = "russian";
// language name in lowercase
static std::string language = "russian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2; return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
} ? 1 : 2;
}
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "КиБ"}, {"KiB", "КиБ"},
{"MiB", "МиБ"}, {"MiB", "МиБ"},
{"GiB", "ГиБ"}, {"GiB", "ГиБ"},
{"building", "строится"}, {"building", "строится"},
{"failed", "неудачный"}, {"failed", "неудачный"},
{"expiring", "истекает"}, {"expiring", "истекает"},
{"established", "работает"}, {"established", "работает"},
{"unknown", "неизвестно"}, {"unknown", "неизвестно"},
{"exploratory", "исследовательский"}, {"exploratory", "исследовательский"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"}, {"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
{"Main page", "Главная"}, {"Main page", "Главная"},
{"Router commands", "Команды роутера"}, {"Router commands", "Команды роутера"},
{"Local Destinations", "Локальные назначения"}, {"Local Destinations", "Локальные назначения"},
{"LeaseSets", "Лизсеты"}, {"LeaseSets", "Лизсеты"},
{"Tunnels", "Туннели"}, {"Tunnels", "Туннели"},
{"Transit Tunnels", "Транзитные туннели"}, {"Transit Tunnels", "Транзитные туннели"},
{"Transports", "Транспорты"}, {"Transports", "Транспорты"},
{"I2P tunnels", "I2P туннели"}, {"I2P tunnels", "I2P туннели"},
{"SAM sessions", "SAM сессии"}, {"SAM sessions", "SAM сессии"},
{"ERROR", "ОШИБКА"}, {"ERROR", "ОШИБКА"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Тестирование"}, {"Testing", "Тестирование"},
{"Firewalled", "Заблокировано извне"}, {"Firewalled", "Заблокировано извне"},
{"Unknown", "Неизвестно"}, {"Unknown", "Неизвестно"},
{"Proxy", "Прокси"}, {"Proxy", "Прокси"},
{"Mesh", "MESH-сеть"}, {"Mesh", "MESH-сеть"},
{"Error", "Ошибка"}, {"Error", "Ошибка"},
{"Clock skew", "Не точное время"}, {"Clock skew", "Не точное время"},
{"Offline", "Оффлайн"}, {"Offline", "Оффлайн"},
{"Symmetric NAT", "Симметричный NAT"}, {"Symmetric NAT", "Симметричный NAT"},
{"Uptime", "В сети"}, {"Uptime", "В сети"},
{"Network status", "Сетевой статус"}, {"Network status", "Сетевой статус"},
{"Network status v6", "Сетевой статус v6"}, {"Network status v6", "Сетевой статус v6"},
{"Stopping in", "Остановка через"}, {"Stopping in", "Остановка через"},
{"Family", "Семейство"}, {"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"}, {"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"}, {"Received", "Получено"},
{"KiB/s", "КиБ/с"}, {"KiB/s", "КиБ/с"},
{"Sent", "Отправлено"}, {"Sent", "Отправлено"},
{"Transit", "Транзит"}, {"Transit", "Транзит"},
{"Data path", "Путь к данным"}, {"Data path", "Путь к данным"},
{"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."}, {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."},
{"Router Ident", "Идентификатор роутера"}, {"Router Ident", "Идентификатор роутера"},
{"Router Family", "Семейство роутера"}, {"Router Family", "Семейство роутера"},
{"Router Caps", "Флаги роутера"}, {"Router Caps", "Флаги роутера"},
{"Version", "Версия"}, {"Version", "Версия"},
{"Our external address", "Наш внешний адрес"}, {"Our external address", "Наш внешний адрес"},
{"supported", "поддерживается"}, {"supported", "поддерживается"},
{"Routers", "Роутеры"}, {"Routers", "Роутеры"},
{"Floodfills", "Флудфилы"}, {"Floodfills", "Флудфилы"},
{"Client Tunnels", "Клиентские туннели"}, {"Client Tunnels", "Клиентские туннели"},
{"Services", "Сервисы"}, {"Services", "Сервисы"},
{"Enabled", "Включено"}, {"Enabled", "Включено"},
{"Disabled", "Выключено"}, {"Disabled", "Выключено"},
{"Encrypted B33 address", "Шифрованные B33 адреса"}, {"Encrypted B33 address", "Шифрованные B33 адреса"},
{"Address registration line", "Строка регистрации адреса"}, {"Address registration line", "Строка регистрации адреса"},
{"Domain", "Домен"}, {"Domain", "Домен"},
{"Generate", "Сгенерировать"}, {"Generate", "Сгенерировать"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."},
{"Address", "Адрес"}, {"Address", "Адрес"},
{"Type", "Тип"}, {"Type", "Тип"},
{"EncType", "ТипШифр"}, {"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"}, {"Inbound tunnels", "Входящие туннели"},
{"ms", "мс"}, {"ms", "мс"},
{"Outbound tunnels", "Исходящие туннели"}, {"Outbound tunnels", "Исходящие туннели"},
{"Tags", "Теги"}, {"Tags", "Теги"},
{"Incoming", "Входящие"}, {"Incoming", "Входящие"},
{"Outgoing", "Исходящие"}, {"Outgoing", "Исходящие"},
{"Destination", "Назначение"}, {"Destination", "Назначение"},
{"Amount", "Количество"}, {"Amount", "Количество"},
{"Incoming Tags", "Входящие теги"}, {"Incoming Tags", "Входящие теги"},
{"Tags sessions", "Сессии тегов"}, {"Tags sessions", "Сессии тегов"},
{"Status", "Статус"}, {"Status", "Статус"},
{"Local Destination", "Локальное назначение"}, {"Local Destination", "Локальное назначение"},
{"Streams", "Стримы"}, {"Streams", "Стримы"},
{"Close stream", "Закрыть стрим"}, {"Close stream", "Закрыть стрим"},
{"I2CP session not found", "I2CP сессия не найдена"}, {"I2CP session not found", "I2CP сессия не найдена"},
{"I2CP is not enabled", "I2CP не включен"}, {"I2CP is not enabled", "I2CP не включен"},
{"Invalid", "Некорректный"}, {"Invalid", "Некорректный"},
{"Store type", "Тип хранилища"}, {"Store type", "Тип хранилища"},
{"Expires", "Истекает"}, {"Expires", "Истекает"},
{"Non Expired Leases", "Не истекшие Lease-ы"}, {"Non Expired Leases", "Не истекшие Lease-ы"},
{"Gateway", "Шлюз"}, {"Gateway", "Шлюз"},
{"TunnelID", "ID туннеля"}, {"TunnelID", "ID туннеля"},
{"EndDate", "Заканчивается"}, {"EndDate", "Заканчивается"},
{"not floodfill", "не флудфил"}, {"not floodfill", "не флудфил"},
{"Queue size", "Размер очереди"}, {"Queue size", "Размер очереди"},
{"Run peer test", "Запустить тестирование"}, {"Run peer test", "Запустить тестирование"},
{"Decline transit tunnels", "Отклонять транзитные туннели"}, {"Decline transit tunnels", "Отклонять транзитные туннели"},
{"Accept transit tunnels", "Принимать транзитные туннели"}, {"Accept transit tunnels", "Принимать транзитные туннели"},
{"Cancel graceful shutdown", "Отменить плавную остановку"}, {"Cancel graceful shutdown", "Отменить плавную остановку"},
{"Start graceful shutdown", "Запустить плавную остановку"}, {"Start graceful shutdown", "Запустить плавную остановку"},
{"Force shutdown", "Принудительная остановка"}, {"Force shutdown", "Принудительная остановка"},
{"Reload external CSS styles", "Перезагрузить внешние CSS стили"}, {"Reload external CSS styles", "Перезагрузить внешние CSS стили"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
{"Logging level", "Уровень логирования"}, {"Logging level", "Уровень логирования"},
{"Transit tunnels limit", "Лимит транзитных туннелей"}, {"Transit tunnels limit", "Лимит транзитных туннелей"},
{"Change", "Изменить"}, {"Change", "Изменить"},
{"Change language", "Изменение языка"}, {"Change language", "Изменение языка"},
{"no transit tunnels currently built", "нет построенных транзитных туннелей"}, {"no transit tunnels currently built", "нет построенных транзитных туннелей"},
{"SAM disabled", "SAM выключен"}, {"SAM disabled", "SAM выключен"},
{"no sessions currently running", "нет запущенных сессий"}, {"no sessions currently running", "нет запущенных сессий"},
{"SAM session not found", "SAM сессия не найдена"}, {"SAM session not found", "SAM сессия не найдена"},
{"SAM Session", "SAM сессия"}, {"SAM Session", "SAM сессия"},
{"Server Tunnels", "Серверные туннели"}, {"Server Tunnels", "Серверные туннели"},
{"Client Forwards", "Клиентские перенаправления"}, {"Client Forwards", "Клиентские перенаправления"},
{"Server Forwards", "Серверные перенаправления"}, {"Server Forwards", "Серверные перенаправления"},
{"Unknown page", "Неизвестная страница"}, {"Unknown page", "Неизвестная страница"},
{"Invalid token", "Неверный токен"}, {"Invalid token", "Неверный токен"},
{"SUCCESS", "УСПЕШНО"}, {"SUCCESS", "УСПЕШНО"},
{"Stream closed", "Стрим закрыт"}, {"Stream closed", "Стрим закрыт"},
{"Stream not found or already was closed", "Стрим не найден или уже закрыт"}, {"Stream not found or already was closed", "Стрим не найден или уже закрыт"},
{"Destination not found", "Точка назначения не найдена"}, {"Destination not found", "Точка назначения не найдена"},
{"StreamID can't be null", "StreamID не может быть пустым"}, {"StreamID can't be null", "StreamID не может быть пустым"},
{"Return to destination page", "Вернуться на страницу точки назначения"}, {"Return to destination page", "Вернуться на страницу точки назначения"},
{"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"}, {"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
{"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"}, {"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"},
{"Back to commands list", "Вернуться к списку команд"}, {"Back to commands list", "Вернуться к списку команд"},
{"Register at reg.i2p", "Зарегистрировать на reg.i2p"}, {"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
{"Description", "Описание"}, {"Description", "Описание"},
{"A bit information about service on domain", "Немного информации о сервисе на домене"}, {"A bit information about service on domain", "Немного информации о сервисе на домене"},
{"Submit", "Отправить"}, {"Submit", "Отправить"},
{"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"}, {"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"},
{"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"}, {"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"},
{"Such destination is not found", "Такая точка назначения не найдена"}, {"Such destination is not found", "Такая точка назначения не найдена"},
{"Unknown command", "Неизвестная команда"}, {"Unknown command", "Неизвестная команда"},
{"Command accepted", "Команда принята"}, {"Command accepted", "Команда принята"},
{"Proxy error", "Ошибка прокси"}, {"Proxy error", "Ошибка прокси"},
{"Proxy info", "Информация прокси"}, {"Proxy info", "Информация прокси"},
{"Proxy error: Host not found", "Ошибка прокси: Узел не найден"}, {"Proxy error: Host not found", "Ошибка прокси: Узел не найден"},
{"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"}, {"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"},
{"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"}, {"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"},
{"Invalid request", "Некорректный запрос"}, {"Invalid request", "Некорректный запрос"},
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"}, {"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
{"addresshelper is not supported", "addresshelper не поддерживается"}, {"addresshelper is not supported", "addresshelper не поддерживается"},
{"Host", "Узел"}, {"Host", "Узел"},
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
{"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"}, {"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"},
{"Continue", "Продолжить"}, {"Continue", "Продолжить"},
{"Addresshelper found", "Найден addresshelper"}, {"Addresshelper found", "Найден addresshelper"},
{"already in router's addressbook", "уже в адресной книге роутера"}, {"already in router's addressbook", "уже в адресной книге роутера"},
{"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"}, {"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"},
{"invalid request uri", "некорректный URI запроса"}, {"invalid request uri", "некорректный URI запроса"},
{"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"}, {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"},
{"Outproxy failure", "Ошибка внешнего прокси"}, {"Outproxy failure", "Ошибка внешнего прокси"},
{"bad outproxy settings", "некорректные настройки внешнего прокси"}, {"bad outproxy settings", "некорректные настройки внешнего прокси"},
{"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"}, {"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"},
{"unknown outproxy url", "неизвестный URL внешнего прокси"}, {"unknown outproxy url", "неизвестный URL внешнего прокси"},
{"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"}, {"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"},
{"hostname too long", "имя хоста слишком длинное"}, {"hostname too long", "имя хоста слишком длинное"},
{"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"}, {"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"},
{"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"}, {"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"},
{"CONNECT error", "Ошибка CONNECT запроса"}, {"CONNECT error", "Ошибка CONNECT запроса"},
{"Failed to Connect", "Не удалось подключиться"}, {"Failed to Connect", "Не удалось подключиться"},
{"socks proxy error", "ошибка SOCKS прокси"}, {"socks proxy error", "ошибка SOCKS прокси"},
{"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"}, {"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"},
{"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"}, {"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"},
{"cannot connect", "не удалось подключиться"}, {"cannot connect", "не удалось подключиться"},
{"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"}, {"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"},
{"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"}, {"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"},
{"Host is down", "Узел недоступен"}, {"Host is down", "Узел недоступен"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"день", "дня", "дней"}}, {"days", {"день", "дня", "дней"}},
{"hours", {"час", "часа", "часов"}}, {"hours", {"час", "часа", "часов"}},
{"minutes", {"минуту", "минуты", "минут"}}, {"minutes", {"минуту", "минуты", "минут"}},
{"seconds", {"секунду", "секунды", "секунд"}}, {"seconds", {"секунду", "секунды", "секунд"}},
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -14,202 +14,200 @@
// Turkmen localization file // Turkmen localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace turkmen // language namespace
{ {
namespace turkmen // language namespace // language name in lowercase
{ static std::string language = "turkmen";
// language name in lowercase
static std::string language = "turkmen";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"KiB", "KiB"},
{"MiB", "MiB"}, {"MiB", "MiB"},
{"GiB", "GiB"}, {"GiB", "GiB"},
{"building", "bina"}, {"building", "bina"},
{"failed", "şowsuz"}, {"failed", "şowsuz"},
{"expiring", "möhleti gutarýar"}, {"expiring", "möhleti gutarýar"},
{"established", "işleýär"}, {"established", "işleýär"},
{"unknown", "näbelli"}, {"unknown", "näbelli"},
{"exploratory", "gözleg"}, {"exploratory", "gözleg"},
{"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"}, {"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"},
{"Main page", "Esasy sahypa"}, {"Main page", "Esasy sahypa"},
{"Router commands", "Marşrutizator buýruklary"}, {"Router commands", "Marşrutizator buýruklary"},
{"Local Destinations", "Ýerli ýerler"}, {"Local Destinations", "Ýerli ýerler"},
{"LeaseSets", "Lizset"}, {"LeaseSets", "Lizset"},
{"Tunnels", "Tuneller"}, {"Tunnels", "Tuneller"},
{"Transit Tunnels", "Tranzit Tunelleri"}, {"Transit Tunnels", "Tranzit Tunelleri"},
{"Transports", "Daşamak"}, {"Transports", "Daşamak"},
{"I2P tunnels", "I2P tuneller"}, {"I2P tunnels", "I2P tuneller"},
{"SAM sessions", "SAM Sessiýasy"}, {"SAM sessions", "SAM Sessiýasy"},
{"ERROR", "Ýalňyşlyk"}, {"ERROR", "Ýalňyşlyk"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Synag etmek"}, {"Testing", "Synag etmek"},
{"Firewalled", "Daşynda petiklendi"}, {"Firewalled", "Daşynda petiklendi"},
{"Unknown", "Näbelli"}, {"Unknown", "Näbelli"},
{"Proxy", "Proksi"}, {"Proxy", "Proksi"},
{"Mesh", "MESH-tor"}, {"Mesh", "MESH-tor"},
{"Error", "Ýalňyşlyk"}, {"Error", "Ýalňyşlyk"},
{"Clock skew", "Takyk wagt däl"}, {"Clock skew", "Takyk wagt däl"},
{"Offline", "Awtonom"}, {"Offline", "Awtonom"},
{"Symmetric NAT", "Simmetriklik NAT"}, {"Symmetric NAT", "Simmetriklik NAT"},
{"Uptime", "Onlaýn onlaýn sözlügi"}, {"Uptime", "Onlaýn onlaýn sözlügi"},
{"Network status", "Tor ýagdaýy"}, {"Network status", "Tor ýagdaýy"},
{"Network status v6", "Tor ýagdaýy v6"}, {"Network status v6", "Tor ýagdaýy v6"},
{"Stopping in", "Soň duruň"}, {"Stopping in", "Soň duruň"},
{"Family", "Maşgala"}, {"Family", "Maşgala"},
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
{"Received", "Alnan"}, {"Received", "Alnan"},
{"KiB/s", "KiB/s"}, {"KiB/s", "KiB/s"},
{"Sent", "Ýerleşdirildi"}, {"Sent", "Ýerleşdirildi"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Maglumat ýoly"}, {"Data path", "Maglumat ýoly"},
{"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."}, {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."},
{"Router Ident", "Marşrutly kesgitleýji"}, {"Router Ident", "Marşrutly kesgitleýji"},
{"Router Family", "Marşrutler maşgalasy"}, {"Router Family", "Marşrutler maşgalasy"},
{"Router Caps", "Baýdaklar marşruteri"}, {"Router Caps", "Baýdaklar marşruteri"},
{"Version", "Wersiýasy"}, {"Version", "Wersiýasy"},
{"Our external address", "Daşarky salgymyz"}, {"Our external address", "Daşarky salgymyz"},
{"supported", "goldanýar"}, {"supported", "goldanýar"},
{"Routers", "Marşrutizatorlar"}, {"Routers", "Marşrutizatorlar"},
{"Floodfills", "Fludfillar"}, {"Floodfills", "Fludfillar"},
{"Client Tunnels", "Müşderi tunelleri"}, {"Client Tunnels", "Müşderi tunelleri"},
{"Services", "Hyzmatlar"}, {"Services", "Hyzmatlar"},
{"Enabled", "Goşuldy"}, {"Enabled", "Goşuldy"},
{"Disabled", "Öçürildi"}, {"Disabled", "Öçürildi"},
{"Encrypted B33 address", "Şifrlenen B33 salgylar"}, {"Encrypted B33 address", "Şifrlenen B33 salgylar"},
{"Address registration line", "Hasaba alyş salgysy"}, {"Address registration line", "Hasaba alyş salgysy"},
{"Domain", "Domen"}, {"Domain", "Domen"},
{"Generate", "Öndürmek"}, {"Generate", "Öndürmek"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Bellik:</b> Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner (example.i2p). Subýutmalary hasaba almak üçin i2pd ulanyň-tools."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Bellik:</b> Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner (example.i2p). Subýutmalary hasaba almak üçin i2pd ulanyň-tools."},
{"Address", "Salgysy"}, {"Address", "Salgysy"},
{"Type", "Görnüş"}, {"Type", "Görnüş"},
{"EncType", "Şifrlemek görnüşi"}, {"EncType", "Şifrlemek görnüşi"},
{"Inbound tunnels", "Gelýän tuneller"}, {"Inbound tunnels", "Gelýän tuneller"},
{"ms", "ms"}, {"ms", "ms"},
{"Outbound tunnels", "Çykýan tuneller"}, {"Outbound tunnels", "Çykýan tuneller"},
{"Tags", "Bellikler"}, {"Tags", "Bellikler"},
{"Incoming", "Gelýän"}, {"Incoming", "Gelýän"},
{"Outgoing", "Çykýan"}, {"Outgoing", "Çykýan"},
{"Destination", "Maksat"}, {"Destination", "Maksat"},
{"Amount", "Sany"}, {"Amount", "Sany"},
{"Incoming Tags", "Gelýän bellikler"}, {"Incoming Tags", "Gelýän bellikler"},
{"Tags sessions", "Sapaklar bellikler"}, {"Tags sessions", "Sapaklar bellikler"},
{"Status", "Ýagdaýy"}, {"Status", "Ýagdaýy"},
{"Local Destination", "Ýerli maksat"}, {"Local Destination", "Ýerli maksat"},
{"Streams", "Strimlary"}, {"Streams", "Strimlary"},
{"Close stream", "Yap strim"}, {"Close stream", "Yap strim"},
{"I2CP session not found", "I2CP Sessiýa tapylmady"}, {"I2CP session not found", "I2CP Sessiýa tapylmady"},
{"I2CP is not enabled", "I2CP goşulmaýar"}, {"I2CP is not enabled", "I2CP goşulmaýar"},
{"Invalid", "Nädogry"}, {"Invalid", "Nädogry"},
{"Store type", "Ammar görnüşi"}, {"Store type", "Ammar görnüşi"},
{"Expires", "Möhleti gutarýar"}, {"Expires", "Möhleti gutarýar"},
{"Non Expired Leases", "Möhleti gutarmady Lizsetlary"}, {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"},
{"Gateway", "Derweze"}, {"Gateway", "Derweze"},
{"TunnelID", "Tuneliň ID"}, {"TunnelID", "Tuneliň ID"},
{"EndDate", "Gutarýar"}, {"EndDate", "Gutarýar"},
{"not floodfill", "fludfil däl"}, {"not floodfill", "fludfil däl"},
{"Queue size", "Nobatyň ululygy"}, {"Queue size", "Nobatyň ululygy"},
{"Run peer test", "Synag başlaň"}, {"Run peer test", "Synag başlaň"},
{"Decline transit tunnels", "Tranzit tunellerini ret ediň"}, {"Decline transit tunnels", "Tranzit tunellerini ret ediň"},
{"Accept transit tunnels", "Tranzit tunellerini alyň"}, {"Accept transit tunnels", "Tranzit tunellerini alyň"},
{"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"}, {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"},
{"Start graceful shutdown", "Tekiz durmak"}, {"Start graceful shutdown", "Tekiz durmak"},
{"Force shutdown", "Mejbury duralga"}, {"Force shutdown", "Mejbury duralga"},
{"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"}, {"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Bellik:</b> Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Bellik:</b> Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."},
{"Logging level", "Giriş derejesi"}, {"Logging level", "Giriş derejesi"},
{"Transit tunnels limit", "Tranzit tunelleriniň çägi"}, {"Transit tunnels limit", "Tranzit tunelleriniň çägi"},
{"Change", "Üýtgetmek"}, {"Change", "Üýtgetmek"},
{"Change language", "Dil üýtgetmek"}, {"Change language", "Dil üýtgetmek"},
{"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"}, {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"},
{"SAM disabled", "SAM öçürilen"}, {"SAM disabled", "SAM öçürilen"},
{"no sessions currently running", "başlamagyň sessiýalary ýok"}, {"no sessions currently running", "başlamagyň sessiýalary ýok"},
{"SAM session not found", "SAM Sessiýa tapylmady"}, {"SAM session not found", "SAM Sessiýa tapylmady"},
{"SAM Session", "SAM Sessiýa"}, {"SAM Session", "SAM Sessiýa"},
{"Server Tunnels", "Serwer tunelleri"}, {"Server Tunnels", "Serwer tunelleri"},
{"Client Forwards", "Müşderi gönükdirýär"}, {"Client Forwards", "Müşderi gönükdirýär"},
{"Server Forwards", "Serweriň täzeden düzlüleri"}, {"Server Forwards", "Serweriň täzeden düzlüleri"},
{"Unknown page", "Näbelli sahypa"}, {"Unknown page", "Näbelli sahypa"},
{"Invalid token", "Nädogry token"}, {"Invalid token", "Nädogry token"},
{"SUCCESS", "Üstünlikli"}, {"SUCCESS", "Üstünlikli"},
{"Stream closed", "Strim ýapyk"}, {"Stream closed", "Strim ýapyk"},
{"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"}, {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"},
{"Destination not found", "Niýetlenen ýeri tapylmady"}, {"Destination not found", "Niýetlenen ýeri tapylmady"},
{"StreamID can't be null", "StreamID boş bolup bilmez"}, {"StreamID can't be null", "StreamID boş bolup bilmez"},
{"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"}, {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"},
{"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"}, {"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"},
{"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"}, {"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"},
{"Back to commands list", "Topar sanawyna dolan"}, {"Back to commands list", "Topar sanawyna dolan"},
{"Register at reg.i2p", "Reg.i2P-de hasaba duruň"}, {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"},
{"Description", "Beýany"}, {"Description", "Beýany"},
{"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"}, {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"},
{"Submit", "Iber"}, {"Submit", "Iber"},
{"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"}, {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"},
{"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"}, {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"},
{"Such destination is not found", "Bu barmaly ýer tapylmady"}, {"Such destination is not found", "Bu barmaly ýer tapylmady"},
{"Unknown command", "Näbelli topar"}, {"Unknown command", "Näbelli topar"},
{"Command accepted", "Topar kabul edilýär"}, {"Command accepted", "Topar kabul edilýär"},
{"Proxy error", "Proksi ýalňyşlygy"}, {"Proxy error", "Proksi ýalňyşlygy"},
{"Proxy info", "Proksi maglumat"}, {"Proxy info", "Proksi maglumat"},
{"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"}, {"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"},
{"Remote host not found in router's addressbook", "Uzakdaky öý eýesi marşruteriň salgy kitabynda tapylmady"}, {"Remote host not found in router's addressbook", "Uzakdaky öý eýesi marşruteriň salgy kitabynda tapylmady"},
{"You may try to find this host on jump services below", "Aşakdaky böküş hyzmatlarynda bu öý eýesini tapmaga synanyşyp bilersiňiz"}, {"You may try to find this host on jump services below", "Aşakdaky böküş hyzmatlarynda bu öý eýesini tapmaga synanyşyp bilersiňiz"},
{"Invalid request", "Nädogry haýyş"}, {"Invalid request", "Nädogry haýyş"},
{"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"}, {"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"},
{"addresshelper is not supported", "Salgylandyryjy goldanok"}, {"addresshelper is not supported", "Salgylandyryjy goldanok"},
{"Host", "Adres"}, {"Host", "Adres"},
{"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"}, {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"},
{"Click here to proceed:", "Dowam etmek bu ýerde basyň:"}, {"Click here to proceed:", "Dowam etmek bu ýerde basyň:"},
{"Continue", "Dowam et"}, {"Continue", "Dowam et"},
{"Addresshelper found", "Forgelper tapyldy"}, {"Addresshelper found", "Forgelper tapyldy"},
{"already in router's addressbook", "marşruteriň adres kitaby"}, {"already in router's addressbook", "marşruteriň adres kitaby"},
{"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"}, {"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"},
{"invalid request uri", "nädogry haýyş URI"}, {"invalid request uri", "nädogry haýyş URI"},
{"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"}, {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"},
{"Outproxy failure", "Daşarky proksi ýalňyşlyk"}, {"Outproxy failure", "Daşarky proksi ýalňyşlyk"},
{"bad outproxy settings", "daşarky daşarky proksi sazlamalary nädogry"}, {"bad outproxy settings", "daşarky daşarky proksi sazlamalary nädogry"},
{"not inside I2P network, but outproxy is not enabled", "I2P torunda däl, ýöne daşarky proksi goşulmaýar"}, {"not inside I2P network, but outproxy is not enabled", "I2P torunda däl, ýöne daşarky proksi goşulmaýar"},
{"unknown outproxy url", "näbelli daşarky proksi URL"}, {"unknown outproxy url", "näbelli daşarky proksi URL"},
{"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"}, {"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"},
{"hostname too long", "hoster eýesi ady gaty uzyn"}, {"hostname too long", "hoster eýesi ady gaty uzyn"},
{"cannot connect to upstream socks proxy", "ýokary jorap SOCKS proksi bilen birigip bolmaýar"}, {"cannot connect to upstream socks proxy", "ýokary jorap SOCKS proksi bilen birigip bolmaýar"},
{"Cannot negotiate with socks proxy", "Iň ýokary jorap SOCKS proksi bilen ylalaşyp bilmeýärler"}, {"Cannot negotiate with socks proxy", "Iň ýokary jorap SOCKS proksi bilen ylalaşyp bilmeýärler"},
{"CONNECT error", "Bagyr haýyşy säwligi"}, {"CONNECT error", "Bagyr haýyşy säwligi"},
{"Failed to Connect", "Birikdirip bilmedi"}, {"Failed to Connect", "Birikdirip bilmedi"},
{"socks proxy error", "socks proksi ýalňyşlygy"}, {"socks proxy error", "socks proksi ýalňyşlygy"},
{"failed to send request to upstream", "öý eýesi proksi üçin haýyş iberip bilmedi"}, {"failed to send request to upstream", "öý eýesi proksi üçin haýyş iberip bilmedi"},
{"No Reply From socks proxy", "Jorap proksi serwerinden hiç hili jogap ýok"}, {"No Reply From socks proxy", "Jorap proksi serwerinden hiç hili jogap ýok"},
{"cannot connect", "birikdirip bilmedi"}, {"cannot connect", "birikdirip bilmedi"},
{"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"}, {"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"},
{"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"}, {"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"},
{"Host is down", "Salgy elýeterli däl"}, {"Host is down", "Salgy elýeterli däl"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"gün", "gün"}}, {"days", {"gün", "gün"}},
{"hours", {"sagat", "sagat"}}, {"hours", {"sagat", "sagat"}},
{"minutes", {"minut", "minut"}}, {"minutes", {"minut", "minut"}},
{"seconds", {"sekunt", "sekunt"}}, {"seconds", {"sekunt", "sekunt"}},
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -14,202 +14,201 @@
// Ukrainian localization file // Ukrainian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace ukrainian // language namespace
{ {
namespace ukrainian // language namespace // language name in lowercase
{ static std::string language = "ukrainian";
// language name in lowercase
static std::string language = "ukrainian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
} ? 1 : 2;
}
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "КіБ"}, {"KiB", "КіБ"},
{"MiB", "МіБ"}, {"MiB", "МіБ"},
{"GiB", "ГіБ"}, {"GiB", "ГіБ"},
{"building", "будується"}, {"building", "будується"},
{"failed", "невдалий"}, {"failed", "невдалий"},
{"expiring", "завершується"}, {"expiring", "завершується"},
{"established", "працює"}, {"established", "працює"},
{"unknown", "невідомо"}, {"unknown", "невідомо"},
{"exploratory", "дослідницький"}, {"exploratory", "дослідницький"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"}, {"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
{"Main page", "Головна"}, {"Main page", "Головна"},
{"Router commands", "Команди маршрутизатора"}, {"Router commands", "Команди маршрутизатора"},
{"Local Destinations", "Локальні Призначення"}, {"Local Destinations", "Локальні Призначення"},
{"LeaseSets", "Лізсети"}, {"LeaseSets", "Лізсети"},
{"Tunnels", "Тунелі"}, {"Tunnels", "Тунелі"},
{"Transit Tunnels", "Транзитні Тунелі"}, {"Transit Tunnels", "Транзитні Тунелі"},
{"Transports", "Транспорти"}, {"Transports", "Транспорти"},
{"I2P tunnels", "I2P тунелі"}, {"I2P tunnels", "I2P тунелі"},
{"SAM sessions", "SAM сесії"}, {"SAM sessions", "SAM сесії"},
{"ERROR", "ПОМИЛКА"}, {"ERROR", "ПОМИЛКА"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Тестування"}, {"Testing", "Тестування"},
{"Firewalled", "Заблоковано ззовні"}, {"Firewalled", "Заблоковано ззовні"},
{"Unknown", "Невідомо"}, {"Unknown", "Невідомо"},
{"Proxy", "Проксі"}, {"Proxy", "Проксі"},
{"Mesh", "MESH-мережа"}, {"Mesh", "MESH-мережа"},
{"Error", "Помилка"}, {"Error", "Помилка"},
{"Clock skew", "Неточний час"}, {"Clock skew", "Неточний час"},
{"Offline", "Офлайн"}, {"Offline", "Офлайн"},
{"Symmetric NAT", "Симетричний NAT"}, {"Symmetric NAT", "Симетричний NAT"},
{"Uptime", "У мережі"}, {"Uptime", "У мережі"},
{"Network status", "Мережевий статус"}, {"Network status", "Мережевий статус"},
{"Network status v6", "Мережевий статус v6"}, {"Network status v6", "Мережевий статус v6"},
{"Stopping in", "Зупинка через"}, {"Stopping in", "Зупинка через"},
{"Family", "Сімейство"}, {"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"}, {"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"}, {"Received", "Отримано"},
{"KiB/s", "КіБ/с"}, {"KiB/s", "КіБ/с"},
{"Sent", "Відправлено"}, {"Sent", "Відправлено"},
{"Transit", "Транзит"}, {"Transit", "Транзит"},
{"Data path", "Шлях до даних"}, {"Data path", "Шлях до даних"},
{"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."}, {"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."},
{"Router Ident", "Ідентифікатор маршрутизатора"}, {"Router Ident", "Ідентифікатор маршрутизатора"},
{"Router Family", "Сімейство маршрутизатора"}, {"Router Family", "Сімейство маршрутизатора"},
{"Router Caps", "Прапорці маршрутизатора"}, {"Router Caps", "Прапорці маршрутизатора"},
{"Version", "Версія"}, {"Version", "Версія"},
{"Our external address", "Наша зовнішня адреса"}, {"Our external address", "Наша зовнішня адреса"},
{"supported", "підтримується"}, {"supported", "підтримується"},
{"Routers", "Маршрутизатори"}, {"Routers", "Маршрутизатори"},
{"Floodfills", "Флудфіли"}, {"Floodfills", "Флудфіли"},
{"Client Tunnels", "Клієнтські Тунелі"}, {"Client Tunnels", "Клієнтські Тунелі"},
{"Services", "Сервіси"}, {"Services", "Сервіси"},
{"Enabled", "Увімкнуто"}, {"Enabled", "Увімкнуто"},
{"Disabled", "Вимкнуто"}, {"Disabled", "Вимкнуто"},
{"Encrypted B33 address", "Шифровані B33 адреси"}, {"Encrypted B33 address", "Шифровані B33 адреси"},
{"Address registration line", "Рядок реєстрації адреси"}, {"Address registration line", "Рядок реєстрації адреси"},
{"Domain", "Домен"}, {"Domain", "Домен"},
{"Generate", "Згенерувати"}, {"Generate", "Згенерувати"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."},
{"Address", "Адреса"}, {"Address", "Адреса"},
{"Type", "Тип"}, {"Type", "Тип"},
{"EncType", "ТипШифр"}, {"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"}, {"Inbound tunnels", "Вхідні тунелі"},
{"ms", "мс"}, {"ms", "мс"},
{"Outbound tunnels", "Вихідні тунелі"}, {"Outbound tunnels", "Вихідні тунелі"},
{"Tags", "Теги"}, {"Tags", "Теги"},
{"Incoming", "Вхідні"}, {"Incoming", "Вхідні"},
{"Outgoing", "Вихідні"}, {"Outgoing", "Вихідні"},
{"Destination", "Призначення"}, {"Destination", "Призначення"},
{"Amount", "Кількість"}, {"Amount", "Кількість"},
{"Incoming Tags", "Вхідні Теги"}, {"Incoming Tags", "Вхідні Теги"},
{"Tags sessions", "Сесії Тегів"}, {"Tags sessions", "Сесії Тегів"},
{"Status", "Статус"}, {"Status", "Статус"},
{"Local Destination", "Локальні Призначення"}, {"Local Destination", "Локальні Призначення"},
{"Streams", "Потоки"}, {"Streams", "Потоки"},
{"Close stream", "Закрити потік"}, {"Close stream", "Закрити потік"},
{"I2CP session not found", "I2CP сесія не знайдена"}, {"I2CP session not found", "I2CP сесія не знайдена"},
{"I2CP is not enabled", "I2CP не увікнуто"}, {"I2CP is not enabled", "I2CP не увікнуто"},
{"Invalid", "Некоректний"}, {"Invalid", "Некоректний"},
{"Store type", "Тип сховища"}, {"Store type", "Тип сховища"},
{"Expires", "Завершується"}, {"Expires", "Завершується"},
{"Non Expired Leases", "Не завершені Lease-и"}, {"Non Expired Leases", "Не завершені Lease-и"},
{"Gateway", "Шлюз"}, {"Gateway", "Шлюз"},
{"TunnelID", "ID тунеля"}, {"TunnelID", "ID тунеля"},
{"EndDate", "Закінчується"}, {"EndDate", "Закінчується"},
{"not floodfill", "не флудфіл"}, {"not floodfill", "не флудфіл"},
{"Queue size", "Розмір черги"}, {"Queue size", "Розмір черги"},
{"Run peer test", "Запустити тестування"}, {"Run peer test", "Запустити тестування"},
{"Decline transit tunnels", "Відхиляти транзитні тунелі"}, {"Decline transit tunnels", "Відхиляти транзитні тунелі"},
{"Accept transit tunnels", "Ухвалювати транзитні тунелі"}, {"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
{"Cancel graceful shutdown", "Скасувати плавну зупинку"}, {"Cancel graceful shutdown", "Скасувати плавну зупинку"},
{"Start graceful shutdown", "Запустити плавну зупинку"}, {"Start graceful shutdown", "Запустити плавну зупинку"},
{"Force shutdown", "Примусова зупинка"}, {"Force shutdown", "Примусова зупинка"},
{"Reload external CSS styles", "Перезавантажити зовнішні стилі CSS"}, {"Reload external CSS styles", "Перезавантажити зовнішні стилі CSS"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
{"Logging level", "Рівень логування"}, {"Logging level", "Рівень логування"},
{"Transit tunnels limit", "Обмеження транзитних тунелів"}, {"Transit tunnels limit", "Обмеження транзитних тунелів"},
{"Change", "Змінити"}, {"Change", "Змінити"},
{"Change language", "Змінити мову"}, {"Change language", "Змінити мову"},
{"no transit tunnels currently built", "немає побудованих транзитних тунелів"}, {"no transit tunnels currently built", "немає побудованих транзитних тунелів"},
{"SAM disabled", "SAM вимкнуто"}, {"SAM disabled", "SAM вимкнуто"},
{"no sessions currently running", "немає запущених сесій"}, {"no sessions currently running", "немає запущених сесій"},
{"SAM session not found", "SAM сесія не знайдена"}, {"SAM session not found", "SAM сесія не знайдена"},
{"SAM Session", "SAM сесія"}, {"SAM Session", "SAM сесія"},
{"Server Tunnels", "Серверні Тунелі"}, {"Server Tunnels", "Серверні Тунелі"},
{"Client Forwards", "Клієнтські Переспрямування"}, {"Client Forwards", "Клієнтські Переспрямування"},
{"Server Forwards", "Серверні Переспрямування"}, {"Server Forwards", "Серверні Переспрямування"},
{"Unknown page", "Невідома сторінка"}, {"Unknown page", "Невідома сторінка"},
{"Invalid token", "Невірний токен"}, {"Invalid token", "Невірний токен"},
{"SUCCESS", "УСПІШНО"}, {"SUCCESS", "УСПІШНО"},
{"Stream closed", "Потік зачинений"}, {"Stream closed", "Потік зачинений"},
{"Stream not found or already was closed", "Потік не знайдений або вже зачинений"}, {"Stream not found or already was closed", "Потік не знайдений або вже зачинений"},
{"Destination not found", "Точка призначення не знайдена"}, {"Destination not found", "Точка призначення не знайдена"},
{"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"}, {"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
{"Return to destination page", "Повернутися на сторінку точки призначення"}, {"Return to destination page", "Повернутися на сторінку точки призначення"},
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"}, {"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
{"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"}, {"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
{"Back to commands list", "Повернутися до списку команд"}, {"Back to commands list", "Повернутися до списку команд"},
{"Register at reg.i2p", "Зареєструвати на reg.i2p"}, {"Register at reg.i2p", "Зареєструвати на reg.i2p"},
{"Description", "Опис"}, {"Description", "Опис"},
{"A bit information about service on domain", "Трохи інформації про сервіс на домені"}, {"A bit information about service on domain", "Трохи інформації про сервіс на домені"},
{"Submit", "Надіслати"}, {"Submit", "Надіслати"},
{"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"}, {"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"},
{"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"}, {"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"},
{"Such destination is not found", "Така точка призначення не знайдена"}, {"Such destination is not found", "Така точка призначення не знайдена"},
{"Unknown command", "Невідома команда"}, {"Unknown command", "Невідома команда"},
{"Command accepted", "Команда прийнята"}, {"Command accepted", "Команда прийнята"},
{"Proxy error", "Помилка проксі"}, {"Proxy error", "Помилка проксі"},
{"Proxy info", "Інформація проксі"}, {"Proxy info", "Інформація проксі"},
{"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"}, {"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"},
{"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"}, {"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"},
{"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"}, {"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
{"Invalid request", "Некоректний запит"}, {"Invalid request", "Некоректний запит"},
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"}, {"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
{"addresshelper is not supported", "addresshelper не підтримується"}, {"addresshelper is not supported", "addresshelper не підтримується"},
{"Host", "Адреса"}, {"Host", "Адреса"},
{"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"}, {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"},
{"Click here to proceed:", "Натисніть тут щоб продовжити:"}, {"Click here to proceed:", "Натисніть тут щоб продовжити:"},
{"Continue", "Продовжити"}, {"Continue", "Продовжити"},
{"Addresshelper found", "Знайдено addresshelper"}, {"Addresshelper found", "Знайдено addresshelper"},
{"already in router's addressbook", "вже в адресній книзі маршрутизатора"}, {"already in router's addressbook", "вже в адресній книзі маршрутизатора"},
{"Click here to update record:", "Натисніть тут щоб оновити запис:"}, {"Click here to update record:", "Натисніть тут щоб оновити запис:"},
{"invalid request uri", "некоректний URI запиту"}, {"invalid request uri", "некоректний URI запиту"},
{"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"}, {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"},
{"Outproxy failure", "Помилка зовнішнього проксі"}, {"Outproxy failure", "Помилка зовнішнього проксі"},
{"bad outproxy settings", "некоректні налаштування зовнішнього проксі"}, {"bad outproxy settings", "некоректні налаштування зовнішнього проксі"},
{"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"}, {"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"},
{"unknown outproxy url", "невідомий URL зовнішнього проксі"}, {"unknown outproxy url", "невідомий URL зовнішнього проксі"},
{"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"}, {"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"},
{"hostname too long", "ім'я вузла надто довге"}, {"hostname too long", "ім'я вузла надто довге"},
{"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"}, {"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"},
{"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"}, {"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"},
{"CONNECT error", "Помилка CONNECT запиту"}, {"CONNECT error", "Помилка CONNECT запиту"},
{"Failed to Connect", "Не вдалося підключитися"}, {"Failed to Connect", "Не вдалося підключитися"},
{"socks proxy error", "помилка SOCKS проксі"}, {"socks proxy error", "помилка SOCKS проксі"},
{"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"}, {"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"},
{"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"}, {"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"},
{"cannot connect", "не вдалося підключитися"}, {"cannot connect", "не вдалося підключитися"},
{"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"}, {"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"},
{"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"}, {"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"},
{"Host is down", "Вузол недоступний"}, {"Host is down", "Вузол недоступний"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"день", "дня", "днів"}}, {"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}}, {"hours", {"годину", "години", "годин"}},
{"minutes", {"хвилину", "хвилини", "хвилин"}}, {"minutes", {"хвилину", "хвилини", "хвилин"}},
{"seconds", {"секунду", "секунди", "секунд"}}, {"seconds", {"секунду", "секунди", "секунд"}},
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -14,202 +14,200 @@
// Ukrainian localization file // Ukrainian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace uzbek // language namespace
{ {
namespace uzbek // language namespace // language name in lowercase
{ static std::string language = "uzbek";
// language name in lowercase
static std::string language = "uzbek";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n > 1 ? 1 : 0; return n > 1 ? 1 : 0;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"KiB", "KiB"},
{"MiB", "MiB"}, {"MiB", "MiB"},
{"GiB", "GiB"}, {"GiB", "GiB"},
{"building", "yaratilmoqda"}, {"building", "yaratilmoqda"},
{"failed", "muvaffaqiyatsiz"}, {"failed", "muvaffaqiyatsiz"},
{"expiring", "muddati tugaydi"}, {"expiring", "muddati tugaydi"},
{"established", "aloqa o'rnatildi"}, {"established", "aloqa o'rnatildi"},
{"unknown", "noma'lum"}, {"unknown", "noma'lum"},
{"exploratory", "tadqiqiy"}, {"exploratory", "tadqiqiy"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> veb-konsoli"}, {"<b>i2pd</b> webconsole", "<b>i2pd</b> veb-konsoli"},
{"Main page", "Asosiy sahifa"}, {"Main page", "Asosiy sahifa"},
{"Router commands", "Router buyruqlari"}, {"Router commands", "Router buyruqlari"},
{"Local Destinations", "Mahalliy joylanishlar"}, {"Local Destinations", "Mahalliy joylanishlar"},
{"LeaseSets", "LeaseSets"}, {"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnellar"}, {"Tunnels", "Tunnellar"},
{"Transit Tunnels", "Tranzit Tunellari"}, {"Transit Tunnels", "Tranzit Tunellari"},
{"Transports", "Transportlar"}, {"Transports", "Transportlar"},
{"I2P tunnels", "I2P tunnellari"}, {"I2P tunnels", "I2P tunnellari"},
{"SAM sessions", "SAM sessiyalari"}, {"SAM sessions", "SAM sessiyalari"},
{"ERROR", "XATO"}, {"ERROR", "XATO"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Testlash"}, {"Testing", "Testlash"},
{"Firewalled", "Xavfsizlik devori bilan himoyalangan"}, {"Firewalled", "Xavfsizlik devori bilan himoyalangan"},
{"Unknown", "Notanish"}, {"Unknown", "Notanish"},
{"Proxy", "Proksi"}, {"Proxy", "Proksi"},
{"Mesh", "Mesh To'r"}, {"Mesh", "Mesh To'r"},
{"Error", "Xato"}, {"Error", "Xato"},
{"Clock skew", "Aniq vaqt emas"}, {"Clock skew", "Aniq vaqt emas"},
{"Offline", "Oflayn"}, {"Offline", "Oflayn"},
{"Symmetric NAT", "Simmetrik NAT"}, {"Symmetric NAT", "Simmetrik NAT"},
{"Uptime", "Ish vaqti"}, {"Uptime", "Ish vaqti"},
{"Network status", "Tarmoq holati"}, {"Network status", "Tarmoq holati"},
{"Network status v6", "Tarmoq holati v6"}, {"Network status v6", "Tarmoq holati v6"},
{"Stopping in", "Ichida to'xtatish"}, {"Stopping in", "Ichida to'xtatish"},
{"Family", "Oila"}, {"Family", "Oila"},
{"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
{"Received", "Qabul qilindi"}, {"Received", "Qabul qilindi"},
{"KiB/s", "KiB/s"}, {"KiB/s", "KiB/s"},
{"Sent", "Yuborilgan"}, {"Sent", "Yuborilgan"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Ma'lumotlar joylanishi"}, {"Data path", "Ma'lumotlar joylanishi"},
{"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."}, {"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."},
{"Router Ident", "Router identifikatori"}, {"Router Ident", "Router identifikatori"},
{"Router Family", "Router oilasi"}, {"Router Family", "Router oilasi"},
{"Router Caps", "Router Bayroqlari"}, {"Router Caps", "Router Bayroqlari"},
{"Version", "Versiya"}, {"Version", "Versiya"},
{"Our external address", "Bizning tashqi manzilimiz"}, {"Our external address", "Bizning tashqi manzilimiz"},
{"supported", "qo'llab-quvvatlanadi"}, {"supported", "qo'llab-quvvatlanadi"},
{"Routers", "Routerlar"}, {"Routers", "Routerlar"},
{"Floodfills", "Floodfills"}, {"Floodfills", "Floodfills"},
{"Client Tunnels", "Mijoz Tunellari"}, {"Client Tunnels", "Mijoz Tunellari"},
{"Services", "Xizmatlar"}, {"Services", "Xizmatlar"},
{"Enabled", "Yoqilgan"}, {"Enabled", "Yoqilgan"},
{"Disabled", "O'chirilgan"}, {"Disabled", "O'chirilgan"},
{"Encrypted B33 address", "Shifrlangan B33 manzil"}, {"Encrypted B33 address", "Shifrlangan B33 manzil"},
{"Address registration line", "Manzilni ro'yxatga olish liniyasi"}, {"Address registration line", "Manzilni ro'yxatga olish liniyasi"},
{"Domain", "Domen"}, {"Domain", "Domen"},
{"Generate", "Yaratish"}, {"Generate", "Yaratish"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Eslatma:</b> natija satridan faqat 2LD domenlarini ro'yxatdan o'tkazish uchun foydalanish mumkin (example.i2p). Subdomenlarni ro'yxatdan o'tkazish uchun 'i2pd-tools'dan foydalaning."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Eslatma:</b> natija satridan faqat 2LD domenlarini ro'yxatdan o'tkazish uchun foydalanish mumkin (example.i2p). Subdomenlarni ro'yxatdan o'tkazish uchun 'i2pd-tools'dan foydalaning."},
{"Address", "Manzil"}, {"Address", "Manzil"},
{"Type", "Turi"}, {"Type", "Turi"},
{"EncType", "ShifrlashTuri"}, {"EncType", "ShifrlashTuri"},
{"Inbound tunnels", "Kirish tunnellari"}, {"Inbound tunnels", "Kirish tunnellari"},
{"ms", "ms"}, {"ms", "ms"},
{"Outbound tunnels", "Chiquvchi tunnellar"}, {"Outbound tunnels", "Chiquvchi tunnellar"},
{"Tags", "Teglar"}, {"Tags", "Teglar"},
{"Incoming", "Kiruvchi"}, {"Incoming", "Kiruvchi"},
{"Outgoing", "Chiquvchi"}, {"Outgoing", "Chiquvchi"},
{"Destination", "Manzilgoh"}, {"Destination", "Manzilgoh"},
{"Amount", "Soni"}, {"Amount", "Soni"},
{"Incoming Tags", "Kiruvchi teglar"}, {"Incoming Tags", "Kiruvchi teglar"},
{"Tags sessions", "Teglar sessiyalari"}, {"Tags sessions", "Teglar sessiyalari"},
{"Status", "Holat"}, {"Status", "Holat"},
{"Local Destination", "Mahalliy joylanish"}, {"Local Destination", "Mahalliy joylanish"},
{"Streams", "Strim"}, {"Streams", "Strim"},
{"Close stream", "Strimni o'chirish"}, {"Close stream", "Strimni o'chirish"},
{"I2CP session not found", "I2CP sessiyasi topilmadi"}, {"I2CP session not found", "I2CP sessiyasi topilmadi"},
{"I2CP is not enabled", "I2CP yoqilmagan"}, {"I2CP is not enabled", "I2CP yoqilmagan"},
{"Invalid", "Noto'g'ri"}, {"Invalid", "Noto'g'ri"},
{"Store type", "Saqlash turi"}, {"Store type", "Saqlash turi"},
{"Expires", "Muddati tugaydi"}, {"Expires", "Muddati tugaydi"},
{"Non Expired Leases", "Muddati O'tmagan Leases"}, {"Non Expired Leases", "Muddati O'tmagan Leases"},
{"Gateway", "Kirish yo'li"}, {"Gateway", "Kirish yo'li"},
{"TunnelID", "TunnelID"}, {"TunnelID", "TunnelID"},
{"EndDate", "Tugash Sanasi"}, {"EndDate", "Tugash Sanasi"},
{"not floodfill", "floodfill emas"}, {"not floodfill", "floodfill emas"},
{"Queue size", "Navbat hajmi"}, {"Queue size", "Navbat hajmi"},
{"Run peer test", "Sinovni boshlang"}, {"Run peer test", "Sinovni boshlang"},
{"Decline transit tunnels", "Tranzit tunnellarini rad etish"}, {"Decline transit tunnels", "Tranzit tunnellarini rad etish"},
{"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"}, {"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"},
{"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qilish"}, {"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qilish"},
{"Start graceful shutdown", "Yumshoq to'xtashni boshlash"}, {"Start graceful shutdown", "Yumshoq to'xtashni boshlash"},
{"Force shutdown", "Majburiy to'xtatish"}, {"Force shutdown", "Majburiy to'xtatish"},
{"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"}, {"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Eslatma:</b> shu yerda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Eslatma:</b> shu yerda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."},
{"Logging level", "Jurnal darajasi"}, {"Logging level", "Jurnal darajasi"},
{"Transit tunnels limit", "Tranzit tunellarning chegarasi"}, {"Transit tunnels limit", "Tranzit tunellarning chegarasi"},
{"Change", "O'zgartirish"}, {"Change", "O'zgartirish"},
{"Change language", "Tilni o'zgartirish"}, {"Change language", "Tilni o'zgartirish"},
{"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"}, {"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"},
{"SAM disabled", "SAM o'chirilgan"}, {"SAM disabled", "SAM o'chirilgan"},
{"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"}, {"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"},
{"SAM session not found", "SAM sessiyasi topilmadi"}, {"SAM session not found", "SAM sessiyasi topilmadi"},
{"SAM Session", "SAM sessiyasi"}, {"SAM Session", "SAM sessiyasi"},
{"Server Tunnels", "Server Tunellari"}, {"Server Tunnels", "Server Tunellari"},
{"Client Forwards", "Mijozlarni Yo'naltirish"}, {"Client Forwards", "Mijozlarni Yo'naltirish"},
{"Server Forwards", "Serverni Yo'naltirish"}, {"Server Forwards", "Serverni Yo'naltirish"},
{"Unknown page", "Noma'lum sahifa"}, {"Unknown page", "Noma'lum sahifa"},
{"Invalid token", "Notogri belgi"}, {"Invalid token", "Notogri belgi"},
{"SUCCESS", "Muvaffaqiyat"}, {"SUCCESS", "Muvaffaqiyat"},
{"Stream closed", "Strim yopiq"}, {"Stream closed", "Strim yopiq"},
{"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"}, {"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"},
{"Destination not found", "Yo'nalish topilmadi"}, {"Destination not found", "Yo'nalish topilmadi"},
{"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"}, {"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"},
{"Return to destination page", "Manzilgoh sahifasiga qaytish"}, {"Return to destination page", "Manzilgoh sahifasiga qaytish"},
{"You will be redirected in 5 seconds", "Siz 5 soniya ichida qayta yo'naltirilasiz"}, {"You will be redirected in 5 seconds", "Siz 5 soniya ichida qayta yo'naltirilasiz"},
{"Transit tunnels count must not exceed 65535", "Tranzit tunnellar soni 65535 dan oshmasligi kerak"}, {"Transit tunnels count must not exceed 65535", "Tranzit tunnellar soni 65535 dan oshmasligi kerak"},
{"Back to commands list", "Buyruqlar ro'yxatiga qaytish"}, {"Back to commands list", "Buyruqlar ro'yxatiga qaytish"},
{"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"}, {"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"},
{"Description", "Tavsif"}, {"Description", "Tavsif"},
{"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"}, {"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"},
{"Submit", "Yuborish"}, {"Submit", "Yuborish"},
{"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"}, {"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"},
{"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"}, {"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"},
{"Such destination is not found", "Bunday yo'nalish topilmadi"}, {"Such destination is not found", "Bunday yo'nalish topilmadi"},
{"Unknown command", "Noma'lum buyruq"}, {"Unknown command", "Noma'lum buyruq"},
{"Command accepted", "Buyruq qabul qilindi"}, {"Command accepted", "Buyruq qabul qilindi"},
{"Proxy error", "Proksi xatosi"}, {"Proxy error", "Proksi xatosi"},
{"Proxy info", "Proksi ma'lumotlari"}, {"Proxy info", "Proksi ma'lumotlari"},
{"Proxy error: Host not found", "Proksi xatosi: Xost topilmadi"}, {"Proxy error: Host not found", "Proksi xatosi: Xost topilmadi"},
{"Remote host not found in router's addressbook", "Masofaviy xost yo'riqnoma manzillar kitobida topilmadi"}, {"Remote host not found in router's addressbook", "Masofaviy xost yo'riqnoma manzillar kitobida topilmadi"},
{"You may try to find this host on jump services below", "Siz xost quyida o'tish xizmatlari orqali topishga harakat qilishingiz mumkin"}, {"You may try to find this host on jump services below", "Siz xost quyida o'tish xizmatlari orqali topishga harakat qilishingiz mumkin"},
{"Invalid request", "Notogri sorov"}, {"Invalid request", "Notogri sorov"},
{"Proxy unable to parse your request", "Proksi sizning so'rovingizni aniqlab ololmayapti"}, {"Proxy unable to parse your request", "Proksi sizning so'rovingizni aniqlab ololmayapti"},
{"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"}, {"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"},
{"Host", "Xost"}, {"Host", "Xost"},
{"added to router's addressbook from helper", "'helper'dan routerning 'addressbook'ga qo'shildi"}, {"added to router's addressbook from helper", "'helper'dan routerning 'addressbook'ga qo'shildi"},
{"Click here to proceed:", "Davom etish uchun shu yerni bosing:"}, {"Click here to proceed:", "Davom etish uchun shu yerni bosing:"},
{"Continue", "Davom etish"}, {"Continue", "Davom etish"},
{"Addresshelper found", "Addresshelper topildi"}, {"Addresshelper found", "Addresshelper topildi"},
{"already in router's addressbook", "allaqachon 'addressbook'da yozilgan"}, {"already in router's addressbook", "allaqachon 'addressbook'da yozilgan"},
{"Click here to update record:", "Yozuvni yangilash uchun shu yerni bosing:"}, {"Click here to update record:", "Yozuvni yangilash uchun shu yerni bosing:"},
{"invalid request uri", "noto'g'ri URI so'rovi"}, {"invalid request uri", "noto'g'ri URI so'rovi"},
{"Can't detect destination host from request", "Sorov orqali manzil xostini aniqlab bo'lmayapti"}, {"Can't detect destination host from request", "Sorov orqali manzil xostini aniqlab bo'lmayapti"},
{"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"}, {"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"},
{"bad outproxy settings", "noto'g'ri tashqi proksi-server sozlamalari"}, {"bad outproxy settings", "noto'g'ri tashqi proksi-server sozlamalari"},
{"not inside I2P network, but outproxy is not enabled", "I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"}, {"not inside I2P network, but outproxy is not enabled", "I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"},
{"unknown outproxy url", "noma'lum outproxy url"}, {"unknown outproxy url", "noma'lum outproxy url"},
{"cannot resolve upstream proxy", "yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"}, {"cannot resolve upstream proxy", "yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"},
{"hostname too long", "xost nomi juda uzun"}, {"hostname too long", "xost nomi juda uzun"},
{"cannot connect to upstream socks proxy", "yuqori 'socks proxy'ga ulanib bo'lmayapti"}, {"cannot connect to upstream socks proxy", "yuqori 'socks proxy'ga ulanib bo'lmayapti"},
{"Cannot negotiate with socks proxy", "'Socks proxy' bilan muzokara olib bo'lmaydi"}, {"Cannot negotiate with socks proxy", "'Socks proxy' bilan muzokara olib bo'lmaydi"},
{"CONNECT error", "CONNECT xatosi"}, {"CONNECT error", "CONNECT xatosi"},
{"Failed to Connect", "Ulanib bo'lmayapti"}, {"Failed to Connect", "Ulanib bo'lmayapti"},
{"socks proxy error", "'socks proxy' xatosi"}, {"socks proxy error", "'socks proxy' xatosi"},
{"failed to send request to upstream", "yuqori http proksi-serveriga so'rovni uborib bo'lmadi"}, {"failed to send request to upstream", "yuqori http proksi-serveriga so'rovni uborib bo'lmadi"},
{"No Reply From socks proxy", "'Socks proxy'dan javob yo'q"}, {"No Reply From socks proxy", "'Socks proxy'dan javob yo'q"},
{"cannot connect", "ulanib bo'lmaydi"}, {"cannot connect", "ulanib bo'lmaydi"},
{"http out proxy not implemented", "tashqi HTTP proksi-serverni qo'llab-quvvatlash amalga oshirilmagan"}, {"http out proxy not implemented", "tashqi HTTP proksi-serverni qo'llab-quvvatlash amalga oshirilmagan"},
{"cannot connect to upstream http proxy", "yuqori http 'proxy-server'iga ulanib bo'lmayapti"}, {"cannot connect to upstream http proxy", "yuqori http 'proxy-server'iga ulanib bo'lmayapti"},
{"Host is down", "Xost ishlamayapti"}, {"Host is down", "Xost ishlamayapti"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Talab qilingan xost bilan aloqa o'rnatilmadi, u ishlamay qolishi mumkin. Iltimos keyinroq qayta urinib ko'ring."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Talab qilingan xost bilan aloqa o'rnatilmadi, u ishlamay qolishi mumkin. Iltimos keyinroq qayta urinib ko'ring."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"kun", "kun"}}, {"days", {"kun", "kun"}},
{"hours", {"soat", "soat"}}, {"hours", {"soat", "soat"}},
{"minutes", {"daqiqa", "daqiqa"}}, {"minutes", {"daqiqa", "daqiqa"}},
{"seconds", {"soniya", "soniya"}}, {"seconds", {"soniya", "soniya"}},
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

@ -11,318 +11,296 @@
#include "Base.h" #include "Base.h"
namespace i2p namespace i2p {
{ namespace data {
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',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'y', 'z', '2', '3', '4', '5', '6', '7',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', };
'y', 'z', '2', '3', '4', '5', '6', '7',
};
const char * GetBase32SubstitutionTable () const char *GetBase32SubstitutionTable() {
{ return T32;
return T32; }
}
static void iT64Build(void); static void iT64Build(void);
/* /*
* *
* BASE64 Substitution Table * BASE64 Substitution Table
* ------------------------- * -------------------------
* *
* Direct Substitution Table * Direct Substitution Table
*/ */
static const char T64[64] = static const char T64[64] =
{ {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '-', '~' '4', '5', '6', '7', '8', '9', '-', '~'
}; };
const char * GetBase64SubstitutionTable () const char *GetBase64SubstitutionTable() {
{ return T64;
return T64; }
}
/* /*
* Reverse Substitution Table (built in run time) * Reverse Substitution Table (built in run time)
*/ */
static char iT64[256]; static char iT64[256];
static int isFirstTime = 1; static int isFirstTime = 1;
/* /*
* Padding * Padding
*/ */
static char P64 = '='; static char P64 = '=';
/* /*
* *
* ByteStreamToBase64 * ByteStreamToBase64
* ------------------ * ------------------
* *
* Converts binary encoded data to BASE64 format. * Converts binary encoded data to BASE64 format.
* *
*/ */
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */ size_t ByteStreamToBase64( /* Number of bytes in the encoded buffer */
const uint8_t * InBuffer, /* Input buffer, binary data */ const uint8_t *InBuffer, /* Input buffer, binary data */
size_t InCount, /* Number of bytes in the input buffer */ size_t InCount, /* Number of bytes in the input buffer */
char * OutBuffer, /* output buffer */ char *OutBuffer, /* output buffer */
size_t len /* length of output buffer */ size_t len /* length of output buffer */
) ) {
{ unsigned char *ps;
unsigned char * ps; unsigned char *pd;
unsigned char * pd; unsigned char acc_1;
unsigned char acc_1; unsigned char acc_2;
unsigned char acc_2; int i;
int i; int n;
int n; int m;
int m; size_t outCount;
size_t outCount;
ps = (unsigned char *)InBuffer; ps = (unsigned char *) InBuffer;
n = InCount / 3; n = InCount / 3;
m = InCount % 3; m = InCount % 3;
if (!m) if (!m)
outCount = 4 * n; outCount = 4 * n;
else else
outCount = 4 * (n + 1); outCount = 4 * (n + 1);
if (outCount > len) return 0; if (outCount > len) return 0;
pd = (unsigned char *)OutBuffer; pd = (unsigned char *) OutBuffer;
for ( i = 0; i < n; i++ ) for (i = 0; i < n; i++) {
{ acc_1 = *ps++;
acc_1 = *ps++; acc_2 = (acc_1 << 4) & 0x30;
acc_2 = (acc_1 << 4) & 0x30; acc_1 >>= 2; /* base64 digit #1 */
acc_1 >>= 2; /* base64 digit #1 */ *pd++ = T64[acc_1];
*pd++ = T64[acc_1]; acc_1 = *ps++;
acc_1 = *ps++; acc_2 |= acc_1 >> 4; /* base64 digit #2 */
acc_2 |= acc_1 >> 4; /* base64 digit #2 */ *pd++ = T64[acc_2];
*pd++ = T64[acc_2]; acc_1 &= 0x0f;
acc_1 &= 0x0f; acc_1 <<= 2;
acc_1 <<= 2; acc_2 = *ps++;
acc_2 = *ps++; acc_1 |= acc_2 >> 6; /* base64 digit #3 */
acc_1 |= acc_2 >> 6; /* base64 digit #3 */ *pd++ = T64[acc_1];
*pd++ = T64[acc_1]; acc_2 &= 0x3f; /* base64 digit #4 */
acc_2 &= 0x3f; /* base64 digit #4 */ *pd++ = T64[acc_2];
*pd++ = T64[acc_2]; }
} if (m == 1) {
if ( m == 1 ) acc_1 = *ps++;
{ acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
acc_1 = *ps++; acc_1 >>= 2; /* base64 digit #1 */
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ *pd++ = T64[acc_1];
acc_1 >>= 2; /* base64 digit #1 */ *pd++ = T64[acc_2];
*pd++ = T64[acc_1]; *pd++ = P64;
*pd++ = T64[acc_2]; *pd++ = P64;
*pd++ = P64;
*pd++ = P64;
} } else if (m == 2) {
else if ( m == 2 ) acc_1 = *ps++;
{ acc_2 = (acc_1 << 4) & 0x3f;
acc_1 = *ps++; acc_1 >>= 2; /* base64 digit #1 */
acc_2 = (acc_1 << 4) & 0x3f; *pd++ = T64[acc_1];
acc_1 >>= 2; /* base64 digit #1 */ acc_1 = *ps++;
*pd++ = T64[acc_1]; acc_2 |= acc_1 >> 4; /* base64 digit #2 */
acc_1 = *ps++; *pd++ = T64[acc_2];
acc_2 |= acc_1 >> 4; /* base64 digit #2 */ acc_1 &= 0x0f;
*pd++ = T64[acc_2]; acc_1 <<= 2; /* base64 digit #3 */
acc_1 &= 0x0f; *pd++ = T64[acc_1];
acc_1 <<= 2; /* base64 digit #3 */ *pd++ = P64;
*pd++ = T64[acc_1]; }
*pd++ = P64;
}
return outCount; return outCount;
} }
/* /*
* *
* Base64ToByteStream * Base64ToByteStream
* ------------------ * ------------------
* *
* Converts BASE64 encoded data to binary format. If input buffer is * Converts BASE64 encoded data to binary format. If input buffer is
* not properly padded, buffer of negative length is returned * not properly padded, buffer of negative length is returned
* *
*/ */
size_t Base64ToByteStream ( /* Number of output bytes */ size_t Base64ToByteStream( /* Number of output bytes */
const char * InBuffer, /* BASE64 encoded buffer */ const char *InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */ size_t InCount, /* Number of input bytes */
uint8_t * OutBuffer, /* output buffer length */ uint8_t *OutBuffer, /* output buffer length */
size_t len /* length of output buffer */ size_t len /* length of output buffer */
) ) {
{ unsigned char *ps;
unsigned char * ps; unsigned char *pd;
unsigned char * pd; unsigned char acc_1;
unsigned char acc_1; unsigned char acc_2;
unsigned char acc_2; int i;
int i; int n;
int n; int m;
int m; size_t outCount;
size_t outCount;
if (isFirstTime) if (isFirstTime)
iT64Build(); iT64Build();
n = InCount / 4; n = InCount / 4;
m = InCount % 4; m = InCount % 4;
if (InCount && !m) if (InCount && !m)
outCount = 3 * n; outCount = 3 * n;
else else
return 0; return 0;
ps = (unsigned char *)(InBuffer + InCount - 1); ps = (unsigned char *) (InBuffer + InCount - 1);
while ( *ps-- == P64 ) while (*ps-- == P64)
outCount--; outCount--;
ps = (unsigned char *)InBuffer; ps = (unsigned char *) InBuffer;
if (outCount > len) if (outCount > len)
return 0; return 0;
pd = OutBuffer; pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount; auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ ) for (i = 0; i < n; i++) {
{ acc_1 = iT64[*ps++];
acc_1 = iT64[*ps++]; acc_2 = iT64[*ps++];
acc_2 = iT64[*ps++]; acc_1 <<= 2;
acc_1 <<= 2; acc_1 |= acc_2 >> 4;
acc_1 |= acc_2 >> 4; *pd++ = acc_1;
*pd++ = acc_1; if (pd >= endOfOutBuffer)
if (pd >= endOfOutBuffer) break;
break;
acc_2 <<= 4; acc_2 <<= 4;
acc_1 = iT64[*ps++]; acc_1 = iT64[*ps++];
acc_2 |= acc_1 >> 2; acc_2 |= acc_1 >> 2;
*pd++ = acc_2; *pd++ = acc_2;
if (pd >= endOfOutBuffer) if (pd >= endOfOutBuffer)
break; break;
acc_2 = iT64[*ps++]; acc_2 = iT64[*ps++];
acc_2 |= acc_1 << 6; acc_2 |= acc_1 << 6;
*pd++ = acc_2; *pd++ = acc_2;
} }
return outCount; return outCount;
} }
size_t Base64EncodingBufferSize (const size_t input_size) size_t Base64EncodingBufferSize(const size_t input_size) {
{ auto d = div(input_size, 3);
auto d = div (input_size, 3); if (d.rem)
if (d.rem) d.quot++;
d.quot++;
return 4 * d.quot; return 4 * d.quot;
} }
std::string ToBase64Standard (const std::string& in) std::string ToBase64Standard(const std::string &in) {
{ auto len = Base64EncodingBufferSize(in.length());
auto len = Base64EncodingBufferSize (in.length ()); char *str = new char[len + 1];
char * str = new char[len + 1]; auto l = ByteStreamToBase64((const uint8_t *) in.c_str(), in.length(), str, len);
auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len); str[l] = 0;
str[l] = 0; // replace '-' by '+' and '~' by '/'
// replace '-' by '+' and '~' by '/' for (size_t i = 0; i < l; i++)
for (size_t i = 0; i < l; i++) if (str[i] == '-')
if (str[i] == '-') str[i] = '+';
str[i] = '+'; else if (str[i] == '~')
else if (str[i] == '~') str[i] = '/';
str[i] = '/';
std::string s(str); std::string s(str);
delete[] str; delete[] str;
return s; return s;
} }
/* /*
* *
* iT64 * iT64
* ---- * ----
* Reverse table builder. P64 character is replaced with 0 * Reverse table builder. P64 character is replaced with 0
* *
* *
*/ */
static void iT64Build() static void iT64Build() {
{ int i;
int i; isFirstTime = 0;
isFirstTime = 0; for (i = 0; i < 256; i++) iT64[i] = -1;
for ( i = 0; i < 256; i++ ) iT64[i] = -1; for (i = 0; i < 64; i++) iT64[(int) T64[i]] = i;
for ( i = 0; i < 64; i++ ) iT64[(int)T64[i]] = i; iT64[(int) P64] = 0;
iT64[(int)P64] = 0; }
}
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) size_t Base32ToByteStream(const char *inBuf, size_t len, uint8_t *outBuf, size_t outLen) {
{ int tmp = 0, bits = 0;
int tmp = 0, bits = 0; size_t ret = 0;
size_t ret = 0; for (size_t i = 0; i < len; i++) {
for (size_t i = 0; i < len; i++) char ch = inBuf[i];
{ if (ch >= '2' && ch <= '7') // digit
char ch = inBuf[i]; ch = (ch - '2') + 26; // 26 means a-z
if (ch >= '2' && ch <= '7') // digit else if (ch >= 'a' && ch <= 'z')
ch = (ch - '2') + 26; // 26 means a-z ch = ch - 'a'; // a = 0
else if (ch >= 'a' && ch <= 'z') else
ch = ch - 'a'; // a = 0 return 0; // unexpected character
else
return 0; // unexpected character
tmp |= ch; tmp |= ch;
bits += 5; bits += 5;
if (bits >= 8) if (bits >= 8) {
{ if (ret >= outLen) return ret;
if (ret >= outLen) return ret; outBuf[ret] = tmp >> (bits - 8);
outBuf[ret] = tmp >> (bits - 8); bits -= 8;
bits -= 8; ret++;
ret++; }
} tmp <<= 5;
tmp <<= 5; }
} return ret;
return ret; }
}
size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) size_t ByteStreamToBase32(const uint8_t *inBuf, size_t len, char *outBuf, size_t outLen) {
{ size_t ret = 0, pos = 1;
size_t ret = 0, pos = 1; int bits = 8, tmp = inBuf[0];
int bits = 8, tmp = inBuf[0]; while (ret < outLen && (bits > 0 || pos < len)) {
while (ret < outLen && (bits > 0 || pos < len)) if (bits < 5) {
{ if (pos < len) {
if (bits < 5) tmp <<= 8;
{ tmp |= inBuf[pos] & 0xFF;
if (pos < len) pos++;
{ bits += 8;
tmp <<= 8; } else // last byte
tmp |= inBuf[pos] & 0xFF; {
pos++; tmp <<= (5 - bits);
bits += 8; bits = 5;
} }
else // last byte }
{
tmp <<= (5 - bits);
bits = 5;
}
}
bits -= 5; bits -= 5;
int ind = (tmp >> bits) & 0x1F; int ind = (tmp >> bits) & 0x1F;
outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2');
ret++; ret++;
} }
return ret; return ret;
} }
} }
} }

View file

@ -14,23 +14,27 @@
#include <iostream> #include <iostream>
namespace i2p { namespace i2p {
namespace data { namespace data {
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); size_t ByteStreamToBase64(const uint8_t *InBuffer, size_t InCount, char *OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase32SubstitutionTable ();
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); size_t Base64ToByteStream(const char *InBuffer, size_t InCount, uint8_t *OutBuffer, size_t len);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
/** const char *GetBase32SubstitutionTable();
* 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 const char *GetBase64SubstitutionTable();
} // 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
*/
size_t Base64EncodingBufferSize(const size_t input_size);
std::string ToBase64Standard(const std::string &in); // using standard table, for Proxy-Authorization
} // data
} // i2p } // i2p
#endif #endif

View file

@ -20,316 +20,310 @@
#include "Signature.h" #include "Signature.h"
#include "Blinding.h" #include "Blinding.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data static EC_POINT *BlindPublicKeyECDSA(const EC_GROUP *group, const EC_POINT *pub, const uint8_t *seed) {
{ BN_CTX *ctx = BN_CTX_new();
static EC_POINT * BlindPublicKeyECDSA (const EC_GROUP * group, const EC_POINT * pub, const uint8_t * seed) BN_CTX_start(ctx);
{ BIGNUM *q = BN_CTX_get(ctx);
BN_CTX * ctx = BN_CTX_new (); EC_GROUP_get_order(group, q, ctx);
BN_CTX_start (ctx); // calculate alpha = seed mod q
BIGNUM * q = BN_CTX_get (ctx); BIGNUM *alpha = BN_CTX_get(ctx);
EC_GROUP_get_order (group, q, ctx); BN_bin2bn(seed, 64, alpha); // seed is in BigEndian
// calculate alpha = seed mod q BN_mod(alpha, alpha, q, ctx); // % q
BIGNUM * alpha = BN_CTX_get (ctx); // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)
BN_bin2bn (seed, 64, alpha); // seed is in BigEndian auto p = EC_POINT_new(group);
BN_mod (alpha, alpha, q, ctx); // % q EC_POINT_mul(group, p, alpha, nullptr, nullptr, ctx); // B*alpha
// A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) EC_POINT_add(group, p, pub, p, ctx); // pub + B*alpha
auto p = EC_POINT_new (group); BN_CTX_end(ctx);
EC_POINT_mul (group, p, alpha, nullptr, nullptr, ctx); // B*alpha BN_CTX_free(ctx);
EC_POINT_add (group, p, pub, p, ctx); // pub + B*alpha return p;
BN_CTX_end (ctx); }
BN_CTX_free (ctx);
return p;
}
static void BlindPrivateKeyECDSA (const EC_GROUP * group, const BIGNUM * priv, const uint8_t * seed, BIGNUM * blindedPriv) static void
{ BlindPrivateKeyECDSA(const EC_GROUP *group, const BIGNUM *priv, const uint8_t *seed, BIGNUM *blindedPriv) {
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
BN_CTX_start (ctx); BN_CTX_start(ctx);
BIGNUM * q = BN_CTX_get (ctx); BIGNUM *q = BN_CTX_get(ctx);
EC_GROUP_get_order (group, q, ctx); EC_GROUP_get_order(group, q, ctx);
// calculate alpha = seed mod q // calculate alpha = seed mod q
BIGNUM * alpha = BN_CTX_get (ctx); 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 BN_mod(alpha, alpha, q, ctx); // % q
BN_add (alpha, alpha, priv); // alpha = alpha + priv 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_mod(blindedPriv, alpha, q, ctx); // % q
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (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) static void
{ BlindEncodedPublicKeyECDSA(size_t publicKeyLen, const EC_GROUP *group, const uint8_t *pub, const uint8_t *seed,
BIGNUM * x = BN_bin2bn (pub, publicKeyLen/2, NULL); uint8_t *blindedPub) {
BIGNUM * y = BN_bin2bn (pub + publicKeyLen/2, publicKeyLen/2, NULL); BIGNUM *x = BN_bin2bn(pub, publicKeyLen / 2, NULL);
EC_POINT * p = EC_POINT_new (group); BIGNUM *y = BN_bin2bn(pub + publicKeyLen / 2, publicKeyLen / 2, NULL);
EC_POINT_set_affine_coordinates_GFp (group, p, x, y, NULL); EC_POINT *p = EC_POINT_new(group);
EC_POINT * p1 = BlindPublicKeyECDSA (group, p, seed); EC_POINT_set_affine_coordinates_GFp(group, p, x, y, NULL);
EC_POINT_free (p); EC_POINT *p1 = BlindPublicKeyECDSA(group, p, seed);
EC_POINT_get_affine_coordinates_GFp (group, p1, x, y, NULL); EC_POINT_free(p);
EC_POINT_free (p1); EC_POINT_get_affine_coordinates_GFp(group, p1, x, y, NULL);
i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); EC_POINT_free(p1);
i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2);
BN_free (x); BN_free (y); i2p::crypto::bn2buf(y, blindedPub + publicKeyLen / 2, publicKeyLen / 2);
} BN_free(x);
BN_free(y);
}
static void BlindEncodedPrivateKeyECDSA (size_t publicKeyLen, const EC_GROUP * group, const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub) static void BlindEncodedPrivateKeyECDSA(size_t publicKeyLen, const EC_GROUP *group, const uint8_t *priv,
{ const uint8_t *seed, uint8_t *blindedPriv, uint8_t *blindedPub) {
BIGNUM * a = BN_bin2bn (priv, publicKeyLen/2, NULL); BIGNUM *a = BN_bin2bn(priv, publicKeyLen / 2, NULL);
BIGNUM * a1 = BN_new (); BIGNUM *a1 = BN_new();
BlindPrivateKeyECDSA (group, a, seed, a1); BlindPrivateKeyECDSA(group, a, seed, a1);
BN_free (a); BN_free(a);
i2p::crypto::bn2buf (a1, blindedPriv, publicKeyLen/2); i2p::crypto::bn2buf(a1, blindedPriv, publicKeyLen / 2);
auto p = EC_POINT_new (group); auto p = EC_POINT_new(group);
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
EC_POINT_mul (group, p, a1, nullptr, nullptr, ctx); // B*a1 EC_POINT_mul(group, p, a1, nullptr, nullptr, ctx); // B*a1
BN_CTX_free (ctx); BN_CTX_free(ctx);
BN_free (a1); BN_free(a1);
BIGNUM * x = BN_new(), * y = BN_new(); BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp (group, p, x, y, NULL); EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL);
EC_POINT_free (p); EC_POINT_free(p);
i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); 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); BN_free(x);
} BN_free(y);
}
template<typename Fn, typename...Args> template<typename Fn, typename...Args>
static size_t BlindECDSA (i2p::data::SigningKeyType sigType, const uint8_t * key, const uint8_t * seed, Fn blind, Args&&...args) static size_t
// blind is BlindEncodedPublicKeyECDSA or BlindEncodedPrivateKeyECDSA BlindECDSA(i2p::data::SigningKeyType sigType, const uint8_t *key, const uint8_t *seed, Fn blind, Args &&...args)
{ // blind is BlindEncodedPublicKeyECDSA or BlindEncodedPrivateKeyECDSA
size_t publicKeyLength = 0; {
EC_GROUP * group = nullptr; size_t publicKeyLength = 0;
switch (sigType) EC_GROUP *group = nullptr;
{ switch (sigType) {
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: 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);
group = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); break;
break; }
} case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: {
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: publicKeyLength = i2p::crypto::ECDSAP384_KEY_LENGTH;
{ group = EC_GROUP_new_by_curve_name(NID_secp384r1);
publicKeyLength = i2p::crypto::ECDSAP384_KEY_LENGTH; break;
group = EC_GROUP_new_by_curve_name (NID_secp384r1); }
break; case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: {
} publicKeyLength = i2p::crypto::ECDSAP521_KEY_LENGTH;
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: group = EC_GROUP_new_by_curve_name(NID_secp521r1);
{ break;
publicKeyLength = i2p::crypto::ECDSAP521_KEY_LENGTH; }
group = EC_GROUP_new_by_curve_name (NID_secp521r1); default:
break; LogPrint(eLogError, "Blinding: Signature type ", (int) sigType, " is not ECDSA");
} }
default: if (group) {
LogPrint (eLogError, "Blinding: Signature type ", (int)sigType, " is not ECDSA"); blind(publicKeyLength, group, key, seed, std::forward<Args>(args)...);
} EC_GROUP_free(group);
if (group) }
{ return publicKeyLength;
blind (publicKeyLength, group, key, seed, std::forward<Args>(args)...); }
EC_GROUP_free (group);
}
return publicKeyLength;
}
//---------------------------------------------------------- //----------------------------------------------------------
const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01; 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; const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth): BlindedPublicKey::BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth) :
m_IsClientAuth (clientAuth) m_IsClientAuth(clientAuth) {
{ if (!identity) return;
if (!identity) return; auto len = identity->GetSigningPublicKeyLen();
auto len = identity->GetSigningPublicKeyLen (); m_PublicKey.resize(len);
m_PublicKey.resize (len); memcpy(m_PublicKey.data(), identity->GetSigningPublicKeyBuffer(), len);
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len); m_SigType = identity->GetSigningKeyType();
m_SigType = identity->GetSigningKeyType (); if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11
m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11 else
else m_BlindedSigType = m_SigType;
m_BlindedSigType = m_SigType; }
}
BlindedPublicKey::BlindedPublicKey (const std::string& b33): BlindedPublicKey::BlindedPublicKey(const std::string &b33) :
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later m_SigType(0) // 0 means invalid, we can't blind DSA, set it later
{ {
uint8_t addr[40]; // TODO: define length from b33 uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); size_t l = i2p::data::Base32ToByteStream(b33.c_str(), b33.length(), addr, 40);
if (l < 32) if (l < 32) {
{ LogPrint(eLogError, "Blinding: Malformed b33 ", b33);
LogPrint (eLogError, "Blinding: Malformed b33 ", b33); return;
return; }
} uint32_t checksum = crc32(0, addr + 3, l - 3);
uint32_t checksum = crc32 (0, addr + 3, l - 3); // checksum is Little Endian
// checksum is Little Endian addr[0] ^= checksum;
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); addr[1] ^= (checksum >> 8);
uint8_t flags = addr[0]; addr[2] ^= (checksum >> 16);
size_t offset = 1; uint8_t flags = addr[0];
if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures size_t offset = 1;
{ if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures
m_SigType = bufbe16toh (addr + offset); offset += 2; {
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2; m_SigType = bufbe16toh(addr + offset);
} offset += 2;
else // one byte sig m_BlindedSigType = bufbe16toh(addr + offset);
{ offset += 2;
m_SigType = addr[offset]; offset++; } else // one byte sig
m_BlindedSigType = addr[offset]; offset++; {
} m_SigType = addr[offset];
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG; offset++;
m_BlindedSigType = addr[offset];
offset++;
}
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG;
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType)); std::unique_ptr<i2p::crypto::Verifier> blindedVerifier(i2p::data::IdentityEx::CreateVerifier(m_SigType));
if (blindedVerifier) if (blindedVerifier) {
{ auto len = blindedVerifier->GetPublicKeyLen();
auto len = blindedVerifier->GetPublicKeyLen (); if (offset + len <= l) {
if (offset + len <= l) m_PublicKey.resize(len);
{ memcpy(m_PublicKey.data(), addr + offset, len);
m_PublicKey.resize (len); } else
memcpy (m_PublicKey.data (), addr + offset, len); LogPrint(eLogError, "Blinding: Public key in b33 address is too short for signature type ",
} (int) m_SigType);
else } else
LogPrint (eLogError, "Blinding: Public key in b33 address is too short for signature type ", (int)m_SigType); LogPrint(eLogError, "Blinding: Unknown signature type ", (int) m_SigType, " in b33");
} }
else
LogPrint (eLogError, "Blinding: Unknown signature type ", (int)m_SigType, " in b33");
}
std::string BlindedPublicKey::ToB33 () const std::string BlindedPublicKey::ToB33() const {
{ if (m_PublicKey.size() > 32) return ""; // assume 25519
if (m_PublicKey.size () > 32) return ""; // assume 25519 uint8_t addr[35];
uint8_t addr[35]; char str[60]; // TODO: define actual length char str[60]; // TODO: define actual length
uint8_t flags = 0; 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[0] = flags; // flags
addr[1] = m_SigType; // sig type addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ()); 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 // checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); addr[0] ^= checksum;
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60); addr[1] ^= (checksum >> 8);
return std::string (str, str + l); 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 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
// stA = signature type of A, 2 bytes big endian uint16_t stA = htobe16(GetSigType());
uint16_t stA = htobe16 (GetSigType ()); // stA1 = signature type of blinded A, 2 bytes big endian
// 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)
// credential = H("credential", A || stA || stA1) H("credential", {{GetPublicKey(), GetPublicKeyLen()},
H ("credential", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, credential); {(const uint8_t *) &stA, 2},
} {(const uint8_t *) &stA1, 2}}, credential);
}
void BlindedPublicKey::GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const void BlindedPublicKey::GetSubcredential(const uint8_t *blinded, size_t len, uint8_t *subcredential) const {
{ uint8_t credential[32];
uint8_t credential[32]; GetCredential(credential);
GetCredential (credential); // subcredential = H("subcredential", credential || blindedPublicKey)
// subcredential = H("subcredential", credential || blindedPublicKey) H("subcredential", {{credential, 32},
H ("subcredential", { {credential, 32}, {blinded, len} }, subcredential); {blinded, len}}, subcredential);
} }
void BlindedPublicKey::GenerateAlpha (const char * date, uint8_t * seed) const void BlindedPublicKey::GenerateAlpha(const char *date, uint8_t *seed) const {
{ uint16_t stA = htobe16(GetSigType()), stA1 = htobe16(GetBlindedSigType());
uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ()); uint8_t salt[32];
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()},
H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt); {(const uint8_t *) &stA, 2},
i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed); {(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 size_t BlindedPublicKey::GetBlindedKey(const char *date, uint8_t *blindedKey) const {
{ uint8_t seed[64];
uint8_t seed[64]; GenerateAlpha(date, seed);
GenerateAlpha (date, seed);
size_t publicKeyLength = 0; size_t publicKeyLength = 0;
switch (m_SigType) switch (m_SigType) {
{ case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: publicKeyLength = BlindECDSA(m_SigType, GetPublicKey(), seed, BlindEncodedPublicKeyECDSA,
publicKeyLength = BlindECDSA (m_SigType, GetPublicKey (), seed, BlindEncodedPublicKeyECDSA, blindedKey); blindedKey);
break; break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
case i2p::data::SIGNING_KEY_TYPE_EDDSA_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; publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break; break;
default: default:
LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType); LogPrint(eLogError, "Blinding: Can't blind signature type ", (int) m_SigType);
} }
return publicKeyLength; return publicKeyLength;
} }
size_t BlindedPublicKey::BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const size_t BlindedPublicKey::BlindPrivateKey(const uint8_t *priv, const char *date, uint8_t *blindedPriv,
{ uint8_t *blindedPub) const {
uint8_t seed[64]; uint8_t seed[64];
GenerateAlpha (date, seed); GenerateAlpha(date, seed);
size_t publicKeyLength = 0; size_t publicKeyLength = 0;
switch (m_SigType) switch (m_SigType) {
{ case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: publicKeyLength = BlindECDSA(m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv,
publicKeyLength = BlindECDSA (m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, blindedPub); blindedPub);
break; break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub); i2p::crypto::GetEd25519()->BlindPrivateKey(priv, seed, blindedPriv, blindedPub);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break; break;
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: {
{ uint8_t exp[64];
uint8_t exp[64]; i2p::crypto::Ed25519::ExpandPrivateKey(priv, exp);
i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp); i2p::crypto::GetEd25519()->BlindPrivateKey(exp, seed, blindedPriv, blindedPub);
i2p::crypto::GetEd25519 ()->BlindPrivateKey (exp, seed, blindedPriv, blindedPub); publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; break;
break; }
} default:
default: LogPrint(eLogError, "Blinding: Can't blind signature type ", (int) m_SigType);
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<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const void BlindedPublicKey::H(const std::string &p, const std::vector<std::pair<const uint8_t *, size_t> > &bufs,
{ uint8_t *hash) const {
SHA256_CTX ctx; SHA256_CTX ctx;
SHA256_Init (&ctx); SHA256_Init(&ctx);
SHA256_Update (&ctx, p.c_str (), p.length ()); 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_Update(&ctx, it.first, it.second);
SHA256_Final (hash, &ctx); SHA256_Final(hash, &ctx);
} }
i2p::data::IdentHash BlindedPublicKey::GetStoreHash (const char * date) const i2p::data::IdentHash BlindedPublicKey::GetStoreHash(const char *date) const {
{ i2p::data::IdentHash hash;
i2p::data::IdentHash hash; uint8_t blinded[128];
uint8_t blinded[128]; size_t publicKeyLength = 0;
size_t publicKeyLength = 0; if (date)
if (date) publicKeyLength = GetBlindedKey(date, blinded);
publicKeyLength = GetBlindedKey (date, blinded); else {
else char currentDate[9];
{ i2p::util::GetCurrentDate(currentDate);
char currentDate[9]; publicKeyLength = GetBlindedKey(currentDate, blinded);
i2p::util::GetCurrentDate (currentDate); }
publicKeyLength = GetBlindedKey (currentDate, blinded); if (publicKeyLength) {
} auto stA1 = htobe16(m_BlindedSigType);
if (publicKeyLength) SHA256_CTX ctx;
{ SHA256_Init(&ctx);
auto stA1 = htobe16 (m_BlindedSigType); SHA256_Update(&ctx, (const uint8_t *) &stA1, 2);
SHA256_CTX ctx; SHA256_Update(&ctx, blinded, publicKeyLength);
SHA256_Init (&ctx); SHA256_Final((uint8_t *) hash, &ctx);
SHA256_Update (&ctx, (const uint8_t *)&stA1, 2); } else
SHA256_Update (&ctx, blinded, publicKeyLength); LogPrint(eLogError, "Blinding: Blinded key type ", (int) m_BlindedSigType, " is not supported");
SHA256_Final ((uint8_t *)hash, &ctx); return hash;
} }
else
LogPrint (eLogError, "Blinding: Blinded key type ", (int)m_BlindedSigType, " is not supported");
return hash;
}
} }
} }

View file

@ -14,42 +14,49 @@
#include <vector> #include <vector>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data class BlindedPublicKey // for encrypted LS2
{ {
class BlindedPublicKey // for encrypted LS2 public:
{
public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false); BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
std::string ToB33 () const;
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; BlindedPublicKey(const std::string &b33); // from b33 without .b32.i2p
size_t GetPublicKeyLen () const { return m_PublicKey.size (); }; std::string ToB33() const;
SigningKeyType GetSigType () const { return m_SigType; };
SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; };
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 const uint8_t *GetPublicKey() const { return m_PublicKey.data(); };
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: size_t GetPublicKeyLen() const { return m_PublicKey.size(); };
void GetCredential (uint8_t * credential) const; // 32 bytes SigningKeyType GetSigType() const { return m_SigType; };
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<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const;
private: SigningKeyType GetBlindedSigType() const { return m_BlindedSigType; };
std::vector<uint8_t> m_PublicKey; bool IsValid() const { return GetSigType(); }; // signature type 0 means invalid
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
bool m_IsClientAuth = false; 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
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
H(const std::string &p, const std::vector<std::pair<const uint8_t *, size_t> > &bufs, uint8_t *hash) const;
private:
std::vector<uint8_t> m_PublicKey;
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
bool m_IsClientAuth = false;
};
}
} }
#endif #endif

View file

@ -11,67 +11,58 @@
#include <array> #include <array>
#include <openssl/sha.h> #include <openssl/sha.h>
namespace i2p namespace i2p {
{ namespace util {
namespace util
{
/** @brief decaying bloom filter implementation */ /** @brief decaying bloom filter implementation */
class DecayingBloomFilter : public IBloomFilter class DecayingBloomFilter : public IBloomFilter {
{ public:
public:
DecayingBloomFilter(const std::size_t size) DecayingBloomFilter(const std::size_t size) {
{ m_Size = size;
m_Size = size; m_Data = new uint8_t[size];
m_Data = new uint8_t[size]; }
}
/** @brief implements IBloomFilter::~IBloomFilter */ /** @brief implements IBloomFilter::~IBloomFilter */
~DecayingBloomFilter() ~DecayingBloomFilter() {
{ delete[] m_Data;
delete [] m_Data; }
}
/** @brief implements IBloomFilter::Add */ /** @brief implements IBloomFilter::Add */
bool Add(const uint8_t * data, std::size_t len) bool Add(const uint8_t *data, std::size_t len) {
{ std::size_t idx;
std::size_t idx; uint8_t mask;
uint8_t mask; Get(data, len, idx, mask);
Get(data, len, idx, mask); if (m_Data[idx] & mask) return false; // filter hit
if(m_Data[idx] & mask) return false; // filter hit m_Data[idx] |= mask;
m_Data[idx] |= mask; return true;
return true; }
}
/** @brief implements IBloomFilter::Decay */ /** @brief implements IBloomFilter::Decay */
void Decay() void Decay() {
{ // reset bloom filter buffer
// reset bloom filter buffer memset(m_Data, 0, m_Size);
memset(m_Data, 0, m_Size); }
}
private: private:
/** @brief get bit index for for data */ /** @brief get bit index for for data */
void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm) void Get(const uint8_t *data, std::size_t len, std::size_t &idx, uint8_t &bm) {
{ bm = 1;
bm = 1; uint8_t digest[32];
uint8_t digest[32]; // TODO: use blake2 because it's faster
// TODO: use blake2 because it's faster SHA256(data, len, digest);
SHA256(data, len, digest); uint64_t i = buf64toh(digest);
uint64_t i = buf64toh(digest); idx = i % m_Size;
idx = i % m_Size; bm <<= (i % 8);
bm <<= (i % 8); }
}
uint8_t * m_Data; uint8_t *m_Data;
std::size_t m_Size; std::size_t m_Size;
}; };
BloomFilterPtr BloomFilter(std::size_t capacity) BloomFilterPtr BloomFilter(std::size_t capacity) {
{ return std::make_shared<DecayingBloomFilter>(capacity);
return std::make_shared<DecayingBloomFilter>(capacity); }
} }
}
} }

View file

@ -8,32 +8,32 @@
#ifndef BLOOM_FILTER_H_ #ifndef BLOOM_FILTER_H_
#define BLOOM_FILTER_H_ #define BLOOM_FILTER_H_
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
namespace i2p namespace i2p {
{ namespace util {
namespace util
{
/** @brief interface for bloom filter */ /** @brief interface for bloom filter */
struct IBloomFilter struct IBloomFilter {
{
/** @brief destructor */ /** @brief destructor */
virtual ~IBloomFilter() {}; virtual ~IBloomFilter() {};
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */
virtual bool Add(const uint8_t * data, std::size_t len) = 0;
/** @brief optionally decay old entries */
virtual void Decay() = 0;
};
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr; /** @brief add entry to bloom filter, return false if filter hit otherwise return true */
virtual bool Add(const uint8_t *data, std::size_t len) = 0;
/** @brief create bloom filter */ /** @brief optionally decay old entries */
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8); virtual void Decay() = 0;
};
} typedef std::shared_ptr <IBloomFilter> BloomFilterPtr;
/** @brief create bloom filter */
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
}
} }
#endif #endif

View file

@ -7,9 +7,11 @@
*/ */
#include "CPU.h" #include "CPU.h"
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
#include <cpuid.h> #include <cpuid.h>
#endif #endif
#include "Log.h" #include "Log.h"
#ifndef bit_AES #ifndef bit_AES
@ -20,39 +22,36 @@
#endif #endif
namespace i2p namespace i2p {
{ namespace cpu {
namespace cpu bool aesni = false;
{ bool avx = false;
bool aesni = false;
bool avx = false;
void Detect(bool AesSwitch, bool AvxSwitch, bool force) void Detect(bool AesSwitch, bool AvxSwitch, bool force) {
{
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
int info[4]; int info[4];
__cpuid(0, info[0], info[1], info[2], info[3]); __cpuid(0, info[0], info[1], info[2], info[3]);
if (info[0] >= 0x00000001) { if (info[0] >= 0x00000001) {
__cpuid(0x00000001, info[0], info[1], info[2], info[3]); __cpuid(0x00000001, info[0], info[1], info[2], info[3]);
#if defined (_WIN32) && (WINVER == 0x0501) // WinXP #if defined (_WIN32) && (WINVER == 0x0501) // WinXP
if (AesSwitch && force) { // only if forced if (AesSwitch && force) { // only if forced
#else #else
if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) { if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) {
#endif #endif
aesni = true; aesni = true;
} }
#if defined (_WIN32) && (WINVER == 0x0501) // WinXP #if defined (_WIN32) && (WINVER == 0x0501) // WinXP
if (AvxSwitch && force) { // only if forced if (AvxSwitch && force) { // only if forced
#else #else
if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) { if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) {
#endif #endif
avx = true; avx = true;
} }
} }
#endif // defined(__x86_64__) || defined(__i386__) #endif // defined(__x86_64__) || defined(__i386__)
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled")); LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled")); LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled"));
} }
} }
} }

View file

@ -9,15 +9,13 @@
#ifndef LIBI2PD_CPU_H #ifndef LIBI2PD_CPU_H
#define LIBI2PD_CPU_H #define LIBI2PD_CPU_H
namespace i2p namespace i2p {
{ namespace cpu {
namespace cpu extern bool aesni;
{ extern bool avx;
extern bool aesni;
extern bool avx;
void Detect(bool AesSwitch, bool AvxSwitch, bool force); void Detect(bool AesSwitch, bool AvxSwitch, bool force);
} }
} }
#endif #endif

View file

@ -13,125 +13,112 @@
#include "ChaCha20.h" #include "ChaCha20.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto namespace chacha {
{ void u32t8le(uint32_t v, uint8_t *p) {
namespace chacha p[0] = v & 0xff;
{ p[1] = (v >> 8) & 0xff;
void u32t8le(uint32_t v, uint8_t * p) 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[2];
value = (value << 8) | p[1]; value = (value << 8) | p[1];
value = (value << 8) | p[0]; 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[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); x[d] = rotl32(x[d] ^ x[a], 16);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); x[c] += x[d];
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); x[b] = rotl32(x[b] ^ x[c], 12);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); 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) void Chacha20Block::operator<<(const Chacha20State &st) {
{ int i;
int i; for (i = 0; i < 16; i++)
for (i = 0; i < 16; i++) u32t8le(st.data[i], data + (i << 2));
u32t8le(st.data[i], data + (i << 2)); }
}
void block (Chacha20State &input, int rounds) void block(Chacha20State &input, int rounds) {
{ int i;
int i; Chacha20State x;
Chacha20State x; x.Copy(input);
x.Copy(input);
for (i = rounds; i > 0; i -= 2) for (i = rounds; i > 0; i -= 2) {
{ quarterround(x.data, 0, 4, 8, 12);
quarterround(x.data, 0, 4, 8, 12); quarterround(x.data, 1, 5, 9, 13);
quarterround(x.data, 1, 5, 9, 13); quarterround(x.data, 2, 6, 10, 14);
quarterround(x.data, 2, 6, 10, 14); quarterround(x.data, 3, 7, 11, 15);
quarterround(x.data, 3, 7, 11, 15); quarterround(x.data, 0, 5, 10, 15);
quarterround(x.data, 0, 5, 10, 15); quarterround(x.data, 1, 6, 11, 12);
quarterround(x.data, 1, 6, 11, 12); quarterround(x.data, 2, 7, 8, 13);
quarterround(x.data, 2, 7, 8, 13); quarterround(x.data, 3, 4, 9, 14);
quarterround(x.data, 3, 4, 9, 14); }
} x += input;
x += input; input.block << x;
input.block << x; }
}
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter) void Chacha20Init(Chacha20State &state, const uint8_t *nonce, const uint8_t *key, uint32_t counter) {
{ state.data[0] = 0x61707865;
state.data[0] = 0x61707865; state.data[1] = 0x3320646e;
state.data[1] = 0x3320646e; state.data[2] = 0x79622d32;
state.data[2] = 0x79622d32; state.data[3] = 0x6b206574;
state.data[3] = 0x6b206574; for (size_t i = 0; i < 8; i++)
for (size_t i = 0; i < 8; i++) state.data[4 + i] = chacha::u8t32le(key + i * 4);
state.data[4 + i] = chacha::u8t32le(key + i * 4);
state.data[12] = htole32 (counter); state.data[12] = htole32 (counter);
for (size_t i = 0; i < 3; i++) for (size_t i = 0; i < 3; i++)
state.data[13 + i] = chacha::u8t32le(nonce + i * 4); state.data[13 + i] = chacha::u8t32le(nonce + i * 4);
} }
void Chacha20SetCounter (Chacha20State& state, uint32_t counter) void Chacha20SetCounter(Chacha20State &state, uint32_t counter) {
{ state.data[12] = htole32 (counter);
state.data[12] = htole32 (counter); state.offset = 0;
state.offset = 0; }
}
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz) void Chacha20Encrypt(Chacha20State &state, uint8_t *buf, size_t sz) {
{ if (state.offset > 0) {
if (state.offset > 0) // previous block if any
{ auto s = chacha::blocksize - state.offset;
// previous block if any if (sz < s) s = sz;
auto s = chacha::blocksize - state.offset; for (size_t i = 0; i < s; i++)
if (sz < s) s = sz; buf[i] ^= state.block.data[state.offset + i];
for (size_t i = 0; i < s; i++) buf += s;
buf[i] ^= state.block.data[state.offset + i]; sz -= s;
buf += s; state.offset += s;
sz -= s; if (state.offset >= chacha::blocksize) state.offset = 0;
state.offset += s; }
if (state.offset >= chacha::blocksize) state.offset = 0; for (size_t i = 0; i < sz; i += chacha::blocksize) {
} chacha::block(state, chacha::rounds);
for (size_t i = 0; i < sz; i += chacha::blocksize) state.data[12]++;
{ for (size_t j = i; j < i + chacha::blocksize; j++) {
chacha::block(state, chacha::rounds); if (j >= sz) {
state.data[12]++; state.offset = j & 0x3F; // % 64
for (size_t j = i; j < i + chacha::blocksize; j++) break;
{ }
if (j >= sz) buf[j] ^= state.block.data[j - i];
{ }
state.offset = j & 0x3F; // % 64 }
break; }
}
buf[j] ^= state.block.data[j - i];
}
}
}
} // namespace chacha } // namespace chacha
} // namespace crypto } // namespace crypto
} // namespace i2p } // namespace i2p
#endif #endif

View file

@ -10,6 +10,7 @@
*/ */
#ifndef LIBI2PD_CHACHA20_H #ifndef LIBI2PD_CHACHA20_H
#define LIBI2PD_CHACHA20_H #define LIBI2PD_CHACHA20_H
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <inttypes.h> #include <inttypes.h>
@ -17,55 +18,55 @@
#include "Crypto.h" #include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p namespace i2p {
{ namespace crypto {
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 namespace chacha {
{ constexpr std::size_t
constexpr std::size_t blocksize = 64; blocksize = 64;
constexpr int rounds = 20; constexpr int rounds = 20;
struct Chacha20State; struct Chacha20State;
struct Chacha20Block
{
Chacha20Block () {};
Chacha20Block (Chacha20Block &&) = delete;
uint8_t data[blocksize]; struct Chacha20Block {
Chacha20Block() {};
void operator << (const Chacha20State & st); Chacha20Block(Chacha20Block &&) = delete;
};
struct Chacha20State uint8_t data[blocksize];
{
Chacha20State (): offset (0) {};
Chacha20State (Chacha20State &&) = delete;
Chacha20State & operator += (const Chacha20State & other) void operator<<(const Chacha20State &st);
{ };
for(int i = 0; i < 16; i++)
data[i] += other.data[i];
return *this;
}
void Copy(const Chacha20State & other) struct Chacha20State {
{ Chacha20State() : offset(0) {};
memcpy(data, other.data, sizeof(uint32_t) * 16);
}
uint32_t data[16];
Chacha20Block block;
size_t offset;
};
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter); Chacha20State(Chacha20State &&) = delete;
void Chacha20SetCounter (Chacha20State& state, uint32_t counter);
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); // encrypt buf in place Chacha20State &operator+=(const Chacha20State &other) {
} // namespace chacha for (int i = 0; i < 16; i++)
} // namespace crypto data[i] += other.data[i];
return *this;
}
void Copy(const Chacha20State &other) {
memcpy(data, other.data, sizeof(uint32_t) * 16);
}
uint32_t data[16];
Chacha20Block block;
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
} // namespace chacha
} // namespace crypto
} // namespace i2p } // namespace i2p
#endif #endif

View file

@ -24,425 +24,471 @@
using namespace boost::program_options; using namespace boost::program_options;
namespace i2p { namespace i2p {
namespace config { namespace config {
options_description m_OptionsDesc; options_description m_OptionsDesc;
variables_map m_Options; variables_map m_Options;
void Init() void Init() {
{ options_description general("General options");
options_description general("General options"); general.add_options()
general.add_options() ("help", "Show this message")
("help", "Show this message") ("version", "Show i2pd version")
("version", "Show i2pd version") ("conf", value<std::string>()->default_value(""),
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") ("tunconf", value<std::string>()->default_value(""),
("tunnelsdir", value<std::string>()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
("certsdir", value<std::string>()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates") ("tunnelsdir", value<std::string>()->default_value(""),
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d")
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") ("certsdir", value<std::string>()->default_value(""),
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates")
("loglevel", value<std::string>()->default_value("warn"), "Set the minimal level of log messages (debug, info, warn, error, none)") ("pidfile", value<std::string>()->default_value(""),
("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)") "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to") ("log", value<std::string>()->default_value(""),
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)") "Logs destination: stdout, file, syslog (stdout if not set)")
("host", value<std::string>()->default_value("0.0.0.0"), "External IP") ("logfile", value<std::string>()->default_value(""),
("ifname", value<std::string>()->default_value(""), "Network interface to bind to") "Path to logfile (stdout if not set, autodetect if daemon)")
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4") ("loglevel", value<std::string>()->default_value("warn"),
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6") "Set the minimal level of log messages (debug, info, warn, error, none)")
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)") ("logclftime", bool_switch()->default_value(false),
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)") "Write full CLF-formatted date and time to log (default: disabled, write only time)")
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)") ("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
("address4", value<std::string>()->default_value(""), "Local address to bind ipv4 transport sockets to") ("datadir", value<std::string>()->default_value(""),
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
("address6", value<std::string>()->default_value(""), "Local address to bind ipv6 transport sockets to") ("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)") ("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2") ("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)") ("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)") ("nat", bool_switch()->default_value(true),
("notransit", bool_switch()->default_value(false), "Router will not accept transit tunnels at startup (default: disabled)") "Should we assume we are behind NAT? (default: enabled)")
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("port", value<uint16_t>()->default_value(0),
("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") "Port to listen for incoming connections (default: auto)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false") ("address4", value<std::string>()->default_value(""),
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") "Local address to bind ipv4 transport sockets to")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored") ("ipv6", bool_switch()->default_value(false),
"Enable communication through ipv6 (default: disabled)")
("address6", value<std::string>()->default_value(""),
"Local address to bind ipv6 transport sockets to")
("reservedrange", bool_switch()->default_value(true),
"Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false),
"Router will go to background after start (default: disabled)")
("service", bool_switch()->default_value(false),
"Router will use system folders like '/var/lib/i2pd' (default: disabled)")
("notransit", bool_switch()->default_value(false),
"Router will not accept transit tunnels at startup (default: disabled)")
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""),
"Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100),
"Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Ignored") ("svcctl", value<std::string>()->default_value(""), "Ignored")
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") ("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask")
#endif #endif
; ;
options_description limits("Limits options"); options_description limits("Limits options");
limits.add_options() limits.add_options()
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.coresize", value<uint32_t>()->default_value(0),
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)") "Maximum size of corefile in Kb (0 - use system limit)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)") ("limits.openfiles", value<uint16_t>()->default_value(0),
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored") "Maximum number of open files (0 - use system default)")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored") ("limits.transittunnels", value<uint16_t>()->default_value(2500),
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored") "Maximum active transit sessions (default:2500)")
; ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored");
options_description httpserver("HTTP Server options"); options_description httpserver("HTTP Server options");
httpserver.add_options() httpserver.add_options()
("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole") ("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole")
("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address") ("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address")
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port") ("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole") ("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole")
("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth") ("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth")
("http.pass", value<std::string>()->default_value(""), "Password for basic auth (default: random, see logs)") ("http.pass", value<std::string>()->default_value(""),
("http.strictheaders", value<bool>()->default_value(true), "Enable strict host checking on WebUI") "Password for basic auth (default: random, see logs)")
("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI") ("http.strictheaders", value<bool>()->default_value(true), "Enable strict host checking on WebUI")
("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )") ("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI")
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )") ("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )")
; ("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )");
options_description httpproxy("HTTP Proxy options"); options_description httpproxy("HTTP Proxy options");
httpproxy.add_options() httpproxy.add_options()
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy") ("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address") ("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port") ("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
("httpproxy.keys", value<std::string>()->default_value("transient-proxy"), "File to persist HTTP Proxy keys. Transient by default") ("httpproxy.keys", value<std::string>()->default_value("transient-proxy"),
("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()-> "File to persist HTTP Proxy keys. Transient by default")
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()->
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length") default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519),
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length") "Signature type for new keys. 7 (EdDSA) by default")
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.inbound.length", value<std::string>()->default_value("3"),
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity") "HTTP proxy inbound tunnel length")
("httpproxy.inbound.lengthVariance", value<std::string>()->default_value("0"), "HTTP proxy inbound tunnels length variance") ("httpproxy.outbound.length", value<std::string>()->default_value("3"),
("httpproxy.outbound.lengthVariance", value<std::string>()->default_value("0"), "HTTP proxy outbound tunnels length variance") "HTTP proxy outbound tunnel length")
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels") ("httpproxy.inbound.quantity", value<std::string>()->default_value("5"),
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels") "HTTP proxy inbound tunnels quantity")
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url") ("httpproxy.outbound.quantity", value<std::string>()->default_value("5"),
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper") "HTTP proxy outbound tunnels quantity")
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("httpproxy.inbound.lengthVariance", value<std::string>()->default_value("0"),
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") "HTTP proxy inbound tunnels length variance")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key") ("httpproxy.outbound.lengthVariance", value<std::string>()->default_value("0"),
; "HTTP proxy outbound tunnels length variance")
("httpproxy.latency.min", value<std::string>()->default_value("0"),
"HTTP proxy min latency for tunnels")
("httpproxy.latency.max", value<std::string>()->default_value("0"),
"HTTP proxy max latency for tunnels")
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"),
"Local destination's LeaseSet type")
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"),
"Local destination's LeaseSet encryption type")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key");
options_description socksproxy("SOCKS Proxy options"); options_description socksproxy("SOCKS Proxy options");
socksproxy.add_options() socksproxy.add_options()
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy") ("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address") ("socksproxy.address", value<std::string>()->default_value("127.0.0.1"),
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port") "SOCKS Proxy listen address")
("socksproxy.keys", value<std::string>()->default_value("transient-proxy"), "File to persist SOCKS Proxy keys. Transient by default") ("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()-> ("socksproxy.keys", value<std::string>()->default_value("transient-proxy"),
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") "File to persist SOCKS Proxy keys. Transient by default")
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length") ("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()->
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length") default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519),
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity") "Signature type for new keys. 7 (EdDSA) by default")
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity") ("socksproxy.inbound.length", value<std::string>()->default_value("3"),
("socksproxy.inbound.lengthVariance", value<std::string>()->default_value("0"), "SOCKS proxy inbound tunnels length variance") "SOCKS proxy inbound tunnel length")
("socksproxy.outbound.lengthVariance", value<std::string>()->default_value("0"), "SOCKS proxy outbound tunnels length variance") ("socksproxy.outbound.length", value<std::string>()->default_value("3"),
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels") "SOCKS proxy outbound tunnel length")
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels") ("socksproxy.inbound.quantity", value<std::string>()->default_value("5"),
("socksproxy.outproxy.enabled", value<bool>()->default_value(false), "Enable or disable SOCKS outproxy") "SOCKS proxy inbound tunnels quantity")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") ("socksproxy.outbound.quantity", value<std::string>()->default_value("5"),
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") "SOCKS proxy outbound tunnels quantity")
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("socksproxy.inbound.lengthVariance", value<std::string>()->default_value("0"),
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") "SOCKS proxy inbound tunnels length variance")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key") ("socksproxy.outbound.lengthVariance", value<std::string>()->default_value("0"),
; "SOCKS proxy outbound tunnels length variance")
("socksproxy.latency.min", value<std::string>()->default_value("0"),
"SOCKS proxy min latency for tunnels")
("socksproxy.latency.max", value<std::string>()->default_value("0"),
"SOCKS proxy max latency for tunnels")
("socksproxy.outproxy.enabled", value<bool>()->default_value(false),
"Enable or disable SOCKS outproxy")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"),
"Upstream outproxy address for SOCKS Proxy")
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050),
"Upstream outproxy port for SOCKS Proxy")
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"),
"Local destination's LeaseSet type")
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"),
"Local destination's LeaseSet encryption type")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""),
"LeaseSet private key");
options_description sam("SAM bridge options"); options_description sam("SAM bridge options");
sam.add_options() sam.add_options()
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge") ("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address") ("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port") ("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread") ("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread");
;
options_description bob("BOB options"); options_description bob("BOB options");
bob.add_options() bob.add_options()
("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel") ("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel")
("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address") ("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address")
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port") ("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port");
;
options_description i2cp("I2CP options"); options_description i2cp("I2CP options");
i2cp.add_options() i2cp.add_options()
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP") ("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address") ("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port") ("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
("i2cp.singlethread", value<bool>()->default_value(true), "Destinations run in the I2CP server's thread") ("i2cp.singlethread", value<bool>()->default_value(true),
; "Destinations run in the I2CP server's thread");
options_description i2pcontrol("I2PControl options"); options_description i2pcontrol("I2PControl options");
i2pcontrol.add_options() i2pcontrol.add_options()
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol") ("i2pcontrol.enabled", value<bool>()->default_value(false),
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address") "Enable or disable I2P Control Protocol")
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port") ("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password") ("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection certificate") ("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key") ("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"),
; "I2PCP connection certificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"),
"I2PCP connection certificate key");
bool upnp_default = false; bool upnp_default = false;
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID))) #if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
upnp_default = true; // enable UPNP for windows GUI and android by default upnp_default = true; // enable UPNP for windows GUI and android by default
#endif #endif
options_description upnp("UPnP options"); options_description upnp("UPnP options");
upnp.add_options() upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding") ("upnp.enabled", value<bool>()->default_value(upnp_default),
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list") "Enable or disable UPnP: automatic port forwarding")
; ("upnp.name", value<std::string>()->default_value("I2Pd"),
"Name i2pd appears in UPnP forwarding list");
options_description precomputation("Precomputation options"); options_description precomputation("Precomputation options");
precomputation.add_options() precomputation.add_options()
("precomputation.elgamal", ("precomputation.elgamal",
#if defined(__x86_64__) #if defined(__x86_64__)
value<bool>()->default_value(false), value<bool>()->default_value(false),
#else #else
value<bool>()->default_value(true), value<bool>()->default_value(true),
#endif #endif
"Enable or disable elgamal precomputation table") "Enable or disable elgamal precomputation table");
;
options_description reseed("Reseed options"); options_description reseed("Reseed options");
reseed.add_options() reseed.add_options()
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature") ("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed") ("reseed.threshold", value<uint16_t>()->default_value(25),
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from") "Minimum number of known routers before requesting reseed")
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") ("reseed.floodfill", value<std::string>()->default_value(""),
("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from") "Path to router info of floodfill to reseed from")
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.file", value<std::string>()->default_value(""),
("reseed.urls", value<std::string>()->default_value( "Path to local .su3 file or HTTPS URL to reseed from")
"https://reseed2.i2p.net/," ("reseed.zipfile", value<std::string>()->default_value(""),
"https://reseed.diva.exchange/," "Path to local .zip file to reseed from")
"https://reseed-fr.i2pd.xyz/," ("reseed.proxy", value<std::string>()->default_value(""),
"https://reseed.memcpy.io/," "url for reseed proxy, supports http/socks")
"https://reseed.onion.im/," ("reseed.urls", value<std::string>()->default_value(
"https://i2pseed.creativecowpat.net:8443/," "https://reseed2.i2p.net/,"
"https://reseed.i2pgit.org/," "https://reseed.diva.exchange/,"
"https://i2p.novg.net/," "https://reseed-fr.i2pd.xyz/,"
"https://banana.incognet.io/," "https://reseed.memcpy.io/,"
"https://reseed-pl.i2pd.xyz/," "https://reseed.onion.im/,"
"https://www2.mk16.de/" "https://i2pseed.creativecowpat.net:8443/,"
), "Reseed URLs, separated by comma") "https://reseed.i2pgit.org/,"
("reseed.yggurls", value<std::string>()->default_value( "https://i2p.novg.net/,"
"http://[324:71e:281a:9ed3::ace]:7070/," "https://banana.incognet.io/,"
"http://[301:65b9:c7cd:9a36::1]:18801/," "https://reseed-pl.i2pd.xyz/,"
"http://[320:8936:ec1a:31f1::216]/," "https://www2.mk16.de/"
"http://[306:3834:97b9:a00a::1]/," ), "Reseed URLs, separated by comma")
"http://[316:f9e0:f22e:a74f::216]/" ("reseed.yggurls", value<std::string>()->default_value(
), "Reseed URLs through the Yggdrasil, separated by comma") "http://[324:71e:281a:9ed3::ace]:7070/,"
; "http://[301:65b9:c7cd:9a36::1]:18801/,"
"http://[320:8936:ec1a:31f1::216]/,"
"http://[306:3834:97b9:a00a::1]/,"
"http://[316:f9e0:f22e:a74f::216]/"
), "Reseed URLs through the Yggdrasil, separated by comma");
options_description addressbook("AddressBook options"); options_description addressbook("AddressBook options");
addressbook.add_options() addressbook.add_options()
("addressbook.enabled", value<bool>()->default_value(true), "Enable address book lookups and subscritions (default: enabled)") ("addressbook.enabled", value<bool>()->default_value(true),
("addressbook.defaulturl", value<std::string>()->default_value( "Enable address book lookups and subscritions (default: enabled)")
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" ("addressbook.defaulturl", value<std::string>()->default_value(
), "AddressBook subscription URL for initial setup") "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
("addressbook.subscriptions", value<std::string>()->default_value( ), "AddressBook subscription URL for initial setup")
"http://reg.i2p/hosts.txt" ("addressbook.subscriptions", value<std::string>()->default_value(
), "AddressBook subscriptions URLs, separated by comma") "http://reg.i2p/hosts.txt"
("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format"); ), "AddressBook subscriptions URLs, separated by comma")
("addressbook.hostsfile", value<std::string>()->default_value(""),
"File to dump addresses in hosts.txt format");
options_description trust("Trust options"); options_description trust("Trust options");
trust.add_options() trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options") ("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Family to trust for first hops") ("trust.family", value<std::string>()->default_value(""), "Router Family to trust for first hops")
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers") ("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?") ("trust.hidden", value<bool>()->default_value(false),
; "Should we hide our router from other routers?");
// Save deprecated websocket options for compatibility // Save deprecated websocket options for compatibility
options_description websocket("Websocket Options"); options_description websocket("Websocket Options");
websocket.add_options() websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "Deprecated option") ("websockets.enabled", value<bool>()->default_value(false), "Deprecated option")
("websockets.address", value<std::string>()->default_value(""), "Deprecated option") ("websockets.address", value<std::string>()->default_value(""), "Deprecated option")
("websockets.port", value<uint16_t>()->default_value(0), "Deprecated option") ("websockets.port", value<uint16_t>()->default_value(0), "Deprecated option");
;
options_description exploratory("Exploratory Options"); options_description exploratory("Exploratory Options");
exploratory.add_options() exploratory.add_options()
("exploratory.inbound.length", value<int>()->default_value(2), "Exploratory inbound tunnel length") ("exploratory.inbound.length", value<int>()->default_value(2), "Exploratory inbound tunnel length")
("exploratory.outbound.length", value<int>()->default_value(2), "Exploratory outbound tunnel length") ("exploratory.outbound.length", value<int>()->default_value(2),
("exploratory.inbound.quantity", value<int>()->default_value(3), "Exploratory inbound tunnels quantity") "Exploratory outbound tunnel length")
("exploratory.outbound.quantity", value<int>()->default_value(3), "Exploratory outbound tunnels quantity") ("exploratory.inbound.quantity", value<int>()->default_value(3),
; "Exploratory inbound tunnels quantity")
("exploratory.outbound.quantity", value<int>()->default_value(3),
"Exploratory outbound tunnels quantity");
options_description ntcp2("NTCP2 Options"); options_description ntcp2("NTCP2 Options");
ntcp2.add_options() ntcp2.add_options()
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)") ("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)") ("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.port", value<uint16_t>()->default_value(0),
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to publish NTCP2 with") "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport") ("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to publish NTCP2 with")
; ("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport");
options_description ssu2("SSU2 Options"); options_description ssu2("SSU2 Options");
ssu2.add_options() ssu2.add_options()
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)") ("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)") ("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)") ("ssu2.port", value<uint16_t>()->default_value(0),
; "Port to listen for incoming SSU2 packets (default: auto)");
options_description nettime("Time sync options"); options_description nettime("Time sync options");
nettime.add_options() nettime.add_options()
("nettime.enabled", value<bool>()->default_value(false), "Disable time sync (default: disabled)") ("nettime.enabled", value<bool>()->default_value(false), "Disable time sync (default: disabled)")
("nettime.ntpservers", value<std::string>()->default_value( ("nettime.ntpservers", value<std::string>()->default_value(
"0.pool.ntp.org," "0.pool.ntp.org,"
"1.pool.ntp.org," "1.pool.ntp.org,"
"2.pool.ntp.org," "2.pool.ntp.org,"
"3.pool.ntp.org" "3.pool.ntp.org"
), "Comma separated list of NTP servers") ), "Comma separated list of NTP servers")
("nettime.ntpsyncinterval", value<int>()->default_value(72), "NTP sync interval in hours (default: 72)") ("nettime.ntpsyncinterval", value<int>()->default_value(72),
("nettime.frompeers", value<bool>()->default_value(true), "Sync clock from transport peers (default: enabled)") "NTP sync interval in hours (default: 72)")
; ("nettime.frompeers", value<bool>()->default_value(true),
"Sync clock from transport peers (default: enabled)");
options_description persist("Network information persisting options"); options_description persist("Network information persisting options");
persist.add_options() persist.add_options()
("persist.profiles", value<bool>()->default_value(true), "Persist peer profiles (default: true)") ("persist.profiles", value<bool>()->default_value(true), "Persist peer profiles (default: true)")
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)") ("persist.addressbook", value<bool>()->default_value(true),
; "Persist full addresses (default: true)");
options_description cpuext("CPU encryption extensions options"); options_description cpuext("CPU encryption extensions options");
cpuext.add_options() cpuext.add_options()
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used") ("cpuext.aesni", bool_switch()->default_value(true),
("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used") "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines") ("cpuext.avx", bool_switch()->default_value(true),
; "Use auto detection for AVX CPU extensions. If false, AVX will be not used")
("cpuext.force", bool_switch()->default_value(false),
"Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines");
options_description meshnets("Meshnet transports options"); options_description meshnets("Meshnet transports options");
meshnets.add_options() meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") ("meshnets.yggdrasil", bool_switch()->default_value(false),
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish") "Support transports through the Yggdrasil (default: false)")
; ("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish");
#ifdef __linux__ #ifdef __linux__
options_description unix_specific("UNIX-specific options"); options_description unix_specific("UNIX-specific options");
unix_specific.add_options() unix_specific.add_options()
("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)") ("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)")
; ;
#endif #endif
m_OptionsDesc m_OptionsDesc
.add(general) .add(general)
.add(limits) .add(limits)
.add(httpserver) .add(httpserver)
.add(httpproxy) .add(httpproxy)
.add(socksproxy) .add(socksproxy)
.add(sam) .add(sam)
.add(bob) .add(bob)
.add(i2cp) .add(i2cp)
.add(i2pcontrol) .add(i2pcontrol)
.add(upnp) .add(upnp)
.add(precomputation) .add(precomputation)
.add(reseed) .add(reseed)
.add(addressbook) .add(addressbook)
.add(trust) .add(trust)
.add(websocket) // deprecated .add(websocket) // deprecated
.add(exploratory) .add(exploratory)
.add(ntcp2) .add(ntcp2)
.add(ssu2) .add(ssu2)
.add(nettime) .add(nettime)
.add(persist) .add(persist)
.add(cpuext) .add(cpuext)
.add(meshnets) .add(meshnets)
#ifdef __linux__ #ifdef __linux__
.add(unix_specific) .add(unix_specific)
#endif #endif
; ;
} }
void ParseCmdline(int argc, char* argv[], bool ignoreUnknown) void ParseCmdline(int argc, char *argv[], bool ignoreUnknown) {
{ try {
try auto style = boost::program_options::command_line_style::unix_style
{ | boost::program_options::command_line_style::allow_long_disguise;
auto style = boost::program_options::command_line_style::unix_style style &= ~boost::program_options::command_line_style::allow_guessing;
| boost::program_options::command_line_style::allow_long_disguise; if (ignoreUnknown)
style &= ~ boost::program_options::command_line_style::allow_guessing; store(command_line_parser(argc, argv).options(m_OptionsDesc).style(
if (ignoreUnknown) style).allow_unregistered().run(), m_Options);
store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options); else
else store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options); }
} catch (boost::program_options::error &e) {
catch (boost::program_options::error& e) ThrowFatal("Error while parsing arguments: ", e.what());
{ std::cerr << "args: " << e.what() << std::endl;
ThrowFatal ("Error while parsing arguments: ", e.what()); exit(EXIT_FAILURE);
std::cerr << "args: " << e.what() << std::endl; }
exit(EXIT_FAILURE);
}
if (!ignoreUnknown && (m_Options.count("help") || m_Options.count("h"))) if (!ignoreUnknown && (m_Options.count("help") || m_Options.count("h"))) {
{ std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; std::cout << m_OptionsDesc;
std::cout << m_OptionsDesc; exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS); } else if (m_Options.count("version")) {
} std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
else if (m_Options.count("version")) std::cout << "Boost version "
{ << BOOST_VERSION / 100000 << "." // maj. version
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; << BOOST_VERSION / 100 % 1000 << "." // min. version
std::cout << "Boost version " << BOOST_VERSION % 100 // patch version
<< BOOST_VERSION / 100000 << "." // maj. version << std::endl;
<< BOOST_VERSION / 100 % 1000 << "." // min. version
<< BOOST_VERSION % 100 // patch version
<< std::endl;
#if defined(OPENSSL_VERSION_TEXT) #if defined(OPENSSL_VERSION_TEXT)
std::cout << OPENSSL_VERSION_TEXT << std::endl; std::cout << OPENSSL_VERSION_TEXT << std::endl;
#endif #endif
#if defined(LIBRESSL_VERSION_TEXT) #if defined(LIBRESSL_VERSION_TEXT)
std::cout << LIBRESSL_VERSION_TEXT << std::endl; std::cout << LIBRESSL_VERSION_TEXT << std::endl;
#endif #endif
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
} }
void ParseConfig(const std::string& path) void ParseConfig(const std::string &path) {
{ if (path == "") return;
if (path == "") return;
std::ifstream config(path, std::ios::in); std::ifstream config(path, std::ios::in);
if (!config.is_open()) if (!config.is_open()) {
{ ThrowFatal("Missing or unreadable config file: ", path);
ThrowFatal ("Missing or unreadable config file: ", path); std::cerr << "missing/unreadable config file: " << path << std::endl;
std::cerr << "missing/unreadable config file: " << path << std::endl; exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
}
try try {
{ store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options);
store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options); }
} catch (boost::program_options::error &e) {
catch (boost::program_options::error& e) ThrowFatal("Error while parsing config file: ", e.what());
{ std::cerr << e.what() << std::endl;
ThrowFatal ("Error while parsing config file: ", e.what()); exit(EXIT_FAILURE);
std::cerr << e.what() << std::endl; };
exit(EXIT_FAILURE); }
};
}
void Finalize() void Finalize() {
{ notify(m_Options);
notify(m_Options); }
}
bool IsDefault(const char *name) bool IsDefault(const char *name) {
{ if (!m_Options.count(name))
if (!m_Options.count(name)) throw "try to check non-existent option";
throw "try to check non-existent option";
if (m_Options[name].defaulted()) if (m_Options[name].defaulted())
return true; return true;
return false; return false;
} }
bool GetOptionAsAny(const char *name, boost::any& value) bool GetOptionAsAny(const char *name, boost::any &value) {
{ if (!m_Options.count(name))
if (!m_Options.count(name)) return false;
return false; value = m_Options[name];
value = m_Options[name]; return true;
return true; }
}
bool GetOptionAsAny(const std::string& name, boost::any& value) bool GetOptionAsAny(const std::string &name, boost::any &value) {
{ return GetOptionAsAny(name.c_str(), value);
return GetOptionAsAny (name.c_str (), value); }
}
} // namespace config } // namespace config
} // namespace i2p } // namespace i2p

View file

@ -25,103 +25,101 @@
*/ */
namespace i2p { namespace i2p {
namespace config { namespace config {
extern boost::program_options::variables_map m_Options; extern boost::program_options::variables_map m_Options;
/** /**
* @brief Initialize list of acceptable parameters * @brief Initialize list of acceptable parameters
* *
* Should be called before any Parse* functions. * Should be called before any Parse* functions.
*/ */
void Init(); void Init();
/** /**
* @brief Parse cmdline parameters, and show help if requested * @brief Parse cmdline parameters, and show help if requested
* @param argc Cmdline arguments count, should be passed from main(). * @param argc Cmdline arguments count, should be passed from main().
* @param argv Cmdline parameters array, 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 * If --help is given in parameters, shows its list with description
* and terminates the program with exitcode 0. * and terminates the program with exitcode 0.
* *
* In case of parameter misuse boost throws an exception. * In case of parameter misuse boost throws an exception.
* We internally handle type boost::program_options::unknown_option, * We internally handle type boost::program_options::unknown_option,
* and then terminate the program with exitcode 1. * and then terminate the program with exitcode 1.
* *
* Other exceptions will be passed to higher level. * Other exceptions will be passed to higher level.
*/ */
void ParseCmdline(int argc, char* argv[], bool ignoreUnknown = false); void ParseCmdline(int argc, char *argv[], bool ignoreUnknown = false);
/** /**
* @brief Load and parse given config file * @brief Load and parse given config file
* @param path Path to config file * @param path Path to config file
* *
* If error occurred when opening file path is points to, * If error occurred when opening file path is points to,
* we show the error message and terminate program. * we show the error message and terminate program.
* *
* In case of parameter misuse boost throws an exception. * In case of parameter misuse boost throws an exception.
* We internally handle type boost::program_options::unknown_option, * We internally handle type boost::program_options::unknown_option,
* and then terminate program with exitcode 1. * and then terminate program with exitcode 1.
* *
* Other exceptions will be passed to higher level. * Other exceptions will be passed to higher level.
*/ */
void ParseConfig(const std::string& path); void ParseConfig(const std::string &path);
/** /**
* @brief Used to combine options from cmdline, config and default values * @brief Used to combine options from cmdline, config and default values
*/ */
void Finalize(); void Finalize();
/** /**
* @brief Accessor to parameters by name * @brief Accessor to parameters by name
* @param name Name of the requested parameter * @param name Name of the requested parameter
* @param value Variable where to store option * @param value Variable where to store option
* @return this function returns false if parameter not found * @return this function returns false if parameter not found
* *
* Example: uint16_t port; GetOption("sam.port", port); * Example: uint16_t port; GetOption("sam.port", port);
*/ */
template<typename T> template<typename T>
bool GetOption(const char *name, T& value) bool GetOption(const char *name, T &value) {
{ if (!m_Options.count(name))
if (!m_Options.count(name)) return false;
return false; value = m_Options[name].as<T>();
value = m_Options[name].as<T>(); return true;
return true; }
}
template<typename T> template<typename T>
bool GetOption(const std::string& name, T& value) bool GetOption(const std::string &name, T &value) {
{ return GetOption(name.c_str(), value);
return GetOption (name.c_str (), value); }
}
bool GetOptionAsAny(const char *name, boost::any& value); bool GetOptionAsAny(const char *name, boost::any &value);
bool GetOptionAsAny(const std::string& 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<typename T>
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 * @brief Set value of given parameter
* @param name Name of checked parameter * @param name Name of settable parameter
* @return true if value set to default, false otherwise * @param value New parameter value
*/ * @return true if value set up successful, false otherwise
bool IsDefault(const char *name); *
} * Example: uint16_t port = 2827; SetOption("bob.port", port);
*/
template<typename T>
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);
}
} }
#endif // CONFIG_H #endif // CONFIG_H

File diff suppressed because it is too large Load diff

View file

@ -50,359 +50,406 @@
# endif # endif
#endif #endif
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len);
{
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
// DSA // DSA
DSA * CreateDSA (); DSA *CreateDSA();
// RSA // RSA
const BIGNUM * GetRSAE (); const BIGNUM *GetRSAE();
// DH // DH
class DHKeys class DHKeys {
{ public:
public:
DHKeys (); DHKeys();
~DHKeys ();
void GenerateKeys (); ~DHKeys();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void Agree (const uint8_t * pub, uint8_t * shared);
private: void GenerateKeys();
DH * m_DH; const uint8_t *GetPublicKey() const { return m_PublicKey; };
uint8_t m_PublicKey[256];
};
// x25519 void Agree(const uint8_t *pub, uint8_t *shared);
class X25519Keys
{
public:
X25519Keys (); private:
X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv
~X25519Keys ();
void GenerateKeys (); DH *m_DH;
const uint8_t * GetPublicKey () const { return m_PublicKey; }; uint8_t m_PublicKey[256];
void GetPrivateKey (uint8_t * priv) const; };
void SetPrivateKey (const uint8_t * priv, bool calculatePublic = false);
bool Agree (const uint8_t * pub, uint8_t * shared);
bool IsElligatorIneligible () const { return m_IsElligatorIneligible; } // x25519
void SetElligatorIneligible () { m_IsElligatorIneligible = true; } class X25519Keys {
public:
private: X25519Keys();
uint8_t m_PublicKey[32]; X25519Keys(const uint8_t *priv, const uint8_t *pub); // if pub is null, derive from priv
~X25519Keys();
void GenerateKeys();
const uint8_t *GetPublicKey() const { return m_PublicKey; };
void GetPrivateKey(uint8_t *priv) const;
void SetPrivateKey(const uint8_t *priv, bool calculatePublic = false);
bool Agree(const uint8_t *pub, uint8_t *shared);
bool IsElligatorIneligible() const { return m_IsElligatorIneligible; }
void SetElligatorIneligible() { m_IsElligatorIneligible = true; }
private:
uint8_t m_PublicKey[32];
#if OPENSSL_X25519 #if OPENSSL_X25519
EVP_PKEY_CTX * m_Ctx; EVP_PKEY_CTX * m_Ctx;
EVP_PKEY * m_Pkey; EVP_PKEY * m_Pkey;
#else #else
BN_CTX * m_Ctx; BN_CTX *m_Ctx;
uint8_t m_PrivateKey[32]; uint8_t m_PrivateKey[32];
#endif #endif
bool m_IsElligatorIneligible = false; // true if definitely ineligible bool m_IsElligatorIneligible = false; // true if definitely ineligible
}; };
// ElGamal // ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted void ElGamalEncrypt(const uint8_t *key, const uint8_t *data,
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub); bool
ElGamalDecrypt(const uint8_t *key, const uint8_t *encrypted, uint8_t *data); // 514 bytes encrypted, 222 data
void GenerateElGamalKeyPair(uint8_t *priv, uint8_t *pub);
// ECIES // ECIES
void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted void ECIESEncrypt(const EC_GROUP *curve, const EC_POINT *key, const uint8_t *data,
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); bool ECIESDecrypt(const EC_GROUP *curve, const BIGNUM *key, const uint8_t *encrypted,
uint8_t *data); // 514 bytes encrypted, 222 data
void GenerateECIESKeyPair(const EC_GROUP *curve, BIGNUM *&priv, EC_POINT *&pub);
// HMAC // HMAC
typedef i2p::data::Tag<32> MACKey; typedef i2p::data::Tag<32> MACKey;
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
// AES void HMACMD5Digest(uint8_t *msg, size_t len, const MACKey &key, uint8_t *digest);
struct ChipherBlock
{
uint8_t buf[16];
void operator^=(const ChipherBlock& other) // XOR // AES
{ struct ChipherBlock {
if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ? uint8_t buf[16];
{
for (int i = 0; i < 4; i++)
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
}
else
{
for (int i = 0; i < 16; i++)
buf[i] ^= other.buf[i];
}
}
};
typedef i2p::data::Tag<32> AESKey; 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<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
} else {
for (int i = 0; i < 16; i++)
buf[i] ^= other.buf[i];
}
}
};
template<size_t sz> typedef i2p::data::Tag<32> AESKey;
class AESAlignedBuffer // 16 bytes alignment
{
public:
AESAlignedBuffer () template<size_t sz>
{ class AESAlignedBuffer // 16 bytes alignment
m_Buf = m_UnalignedBuffer; {
uint8_t rem = ((size_t)m_Buf) & 0x0f; public:
if (rem)
m_Buf += (16 - rem);
}
operator uint8_t * () { return m_Buf; }; AESAlignedBuffer() {
operator const uint8_t * () const { return m_Buf; }; m_Buf = m_UnalignedBuffer;
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; }; uint8_t rem = ((size_t) m_Buf) & 0x0f;
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; }; if (rem)
m_Buf += (16 - rem);
}
private: operator uint8_t *() { return m_Buf; };
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment operator const uint8_t *() const { return m_Buf; };
uint8_t * m_Buf;
}; ChipherBlock *GetChipherBlock() { return (ChipherBlock *) m_Buf; };
const ChipherBlock *GetChipherBlock() const { return (const ChipherBlock *) m_Buf; };
private:
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
uint8_t *m_Buf;
};
#ifdef __AES__ #ifdef __AES__
class ECBCryptoAESNI class ECBCryptoAESNI
{ {
public: public:
uint8_t * GetKeySchedule () { return m_KeySchedule; }; uint8_t * GetKeySchedule () { return m_KeySchedule; };
protected: protected:
void ExpandKey (const AESKey& key); void ExpandKey (const AESKey& key);
private: private:
AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes
}; };
#endif #endif
#ifdef __AES__ #ifdef __AES__
class ECBEncryption: public ECBCryptoAESNI class ECBEncryption: public ECBCryptoAESNI
#else #else
class ECBEncryption
class ECBEncryption
#endif #endif
{ {
public: public:
void SetKey (const AESKey& key); void SetKey(const AESKey &key);
void Encrypt(const ChipherBlock * in, ChipherBlock * out); void Encrypt(const ChipherBlock *in, ChipherBlock *out);
private: private:
AES_KEY m_Key; AES_KEY m_Key;
}; };
#ifdef __AES__ #ifdef __AES__
class ECBDecryption: public ECBCryptoAESNI class ECBDecryption: public ECBCryptoAESNI
#else #else
class ECBDecryption
class ECBDecryption
#endif #endif
{ {
public: public:
void SetKey (const AESKey& key); void SetKey(const AESKey &key);
void Decrypt (const ChipherBlock * in, ChipherBlock * out);
private:
AES_KEY m_Key;
};
class CBCEncryption void Decrypt(const ChipherBlock *in, ChipherBlock *out);
{
public:
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); }; private:
AES_KEY m_Key;
};
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes class CBCEncryption {
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes public:
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); };
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); CBCEncryption() { memset((uint8_t *) m_LastBlock, 0, 16); };
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Encrypt (const uint8_t * in, uint8_t * out); // one block
ECBEncryption & ECB() { return m_ECBEncryption; } void SetKey(const AESKey &key) { m_ECBEncryption.SetKey(key); }; // 32 bytes
void SetIV(const uint8_t *iv) { memcpy((uint8_t *) m_LastBlock, iv, 16); }; // 16 bytes
void GetIV(uint8_t *iv) const { memcpy(iv, (const uint8_t *) m_LastBlock, 16); };
private: void Encrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
AESAlignedBuffer<16> m_LastBlock; void Encrypt(const uint8_t *in, std::size_t len, uint8_t *out);
ECBEncryption m_ECBEncryption; void Encrypt(const uint8_t *in, uint8_t *out); // one block
};
class CBCDecryption ECBEncryption &ECB() { return m_ECBEncryption; }
{
public:
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); }; private:
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes AESAlignedBuffer<16> m_LastBlock;
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); };
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); ECBEncryption m_ECBEncryption;
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); };
void Decrypt (const uint8_t * in, uint8_t * out); // one block
ECBDecryption & ECB() { return m_ECBDecryption; } class CBCDecryption {
public:
private: CBCDecryption() { memset((uint8_t *) m_IV, 0, 16); };
AESAlignedBuffer<16> m_IV; void SetKey(const AESKey &key) { m_ECBDecryption.SetKey(key); }; // 32 bytes
ECBDecryption m_ECBDecryption; void SetIV(const uint8_t *iv) { memcpy((uint8_t *) m_IV, iv, 16); }; // 16 bytes
}; void GetIV(uint8_t *iv) const { memcpy(iv, (const uint8_t *) m_IV, 16); };
class TunnelEncryption // with double IV encryption void Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey) void Decrypt(const uint8_t *in, std::size_t len, uint8_t *out);
{
m_LayerEncryption.SetKey (layerKey);
m_IVEncryption.SetKey (ivKey);
}
void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data) void Decrypt(const uint8_t *in, uint8_t *out); // one block
private: ECBDecryption &ECB() { return m_ECBDecryption; }
ECBEncryption m_IVEncryption; private:
CBCEncryption m_LayerEncryption;
};
class TunnelDecryption // with double IV encryption AESAlignedBuffer<16> m_IV;
{ ECBDecryption m_ECBDecryption;
public: };
void SetKeys (const AESKey& layerKey, const AESKey& ivKey) class TunnelEncryption // with double IV encryption
{ {
m_LayerDecryption.SetKey (layerKey); public:
m_IVDecryption.SetKey (ivKey);
}
void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data) void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
m_LayerEncryption.SetKey(layerKey);
m_IVEncryption.SetKey(ivKey);
}
private: void Encrypt(const uint8_t *in, uint8_t *out); // 1024 bytes (16 IV + 1008 data)
ECBDecryption m_IVDecryption; private:
CBCDecryption m_LayerDecryption;
}; ECBEncryption m_IVEncryption;
CBCEncryption m_LayerEncryption;
};
class TunnelDecryption // with double IV encryption
{
public:
void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
m_LayerDecryption.SetKey(layerKey);
m_IVDecryption.SetKey(ivKey);
}
void Decrypt(const uint8_t *in, uint8_t *out); // 1024 bytes (16 IV + 1008 data)
private:
ECBDecryption m_IVDecryption;
CBCDecryption m_LayerDecryption;
};
// AEAD/ChaCha20/Poly1305 // AEAD/ChaCha20/Poly1305
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag bool
AEADChaCha20Poly1305(const uint8_t *msg, size_t msgLen, const uint8_t *ad, size_t adLen, const uint8_t *key,
const uint8_t *nonce, uint8_t *buf, size_t len, bool encrypt); // msgLen is len without tag
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad void AEADChaCha20Poly1305Encrypt(const std::vector<std::pair<uint8_t *, size_t> > &bufs, const uint8_t *key,
const uint8_t *nonce, uint8_t *mac); // encrypt multiple buffers with zero ad
// ChaCha20 // ChaCha20
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); void ChaCha20(const uint8_t *msg, size_t msgLen, const uint8_t *key, const uint8_t *nonce, uint8_t *out);
// HKDF // 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
// Noise // Noise
struct NoiseSymmetricState struct NoiseSymmetricState {
{ uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
void MixHash (const uint8_t * buf, size_t len); void MixHash(const uint8_t *buf, size_t len);
void MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs);
void MixKey (const uint8_t * sharedSecret);
};
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router) void MixHash(const std::vector<std::pair<uint8_t *, size_t> > &bufs);
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2)
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2) void MixKey(const uint8_t *sharedSecret);
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) };
void InitNoiseNState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_N (tunnels, router)
void InitNoiseXKState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_XK (NTCP2)
void InitNoiseXKState1(NoiseSymmetricState &state, const uint8_t *pub); // Noise_XK (SSU2)
void InitNoiseIKState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_IK (ratchets)
// init and terminate // init and terminate
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force); void InitCrypto(bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto ();
} void TerminateCrypto();
}
} }
// take care about openssl below 1.1.0 // take care about openssl below 1.1.0
#if LEGACY_OPENSSL #if LEGACY_OPENSSL
// define getters and setters introduced in 1.1.0 // define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
{ if (d->p) BN_free(d->p);
if (d->p) BN_free (d->p); if (d->q) BN_free(d->q);
if (d->q) BN_free (d->q); if (d->g) BN_free(d->g);
if (d->g) BN_free (d->g); d->p = p;
d->p = p; d->q = q; d->g = g; return 1; d->q = q;
} d->g = g;
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) return 1;
{ }
if (d->pub_key) BN_free (d->pub_key);
if (d->priv_key) BN_free (d->priv_key);
d->pub_key = pub_key; d->priv_key = priv_key; return 1;
}
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (sig->r) BN_free (sig->r);
if (sig->s) BN_free (sig->s);
sig->r = r; sig->s = s; return 1;
}
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) {
{ if (d->pub_key) BN_free(d->pub_key);
if (sig->r) BN_free (sig->r); if (d->priv_key) BN_free(d->priv_key);
if (sig->s) BN_free (sig->s); d->pub_key = pub_key;
sig->r = r; sig->s = s; return 1; d->priv_key = priv_key;
} return 1;
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) }
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) {
{ *pub_key = d->pub_key;
if (r->n) BN_free (r->n); *priv_key = d->priv_key;
if (r->e) BN_free (r->e); }
if (r->d) BN_free (r->d);
r->n = n; r->e = e; r->d = d; return 1;
}
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
{ *n = r->n; *e = r->e; *d = r->d; }
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
{ if (sig->r) BN_free(sig->r);
if (dh->p) BN_free (dh->p); if (sig->s) BN_free(sig->s);
if (dh->q) BN_free (dh->q); sig->r = r;
if (dh->g) BN_free (dh->g); sig->s = s;
dh->p = p; dh->q = q; dh->g = g; return 1; return 1;
} }
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
{
if (dh->pub_key) BN_free (dh->pub_key);
if (dh->priv_key) BN_free (dh->priv_key);
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
}
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
{ return pkey->pkey.rsa; } *pr = sig->r;
*ps = sig->s;
}
inline EVP_MD_CTX *EVP_MD_CTX_new () inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
{ return EVP_MD_CTX_create(); } if (sig->r) BN_free(sig->r);
inline void EVP_MD_CTX_free (EVP_MD_CTX *ctx) if (sig->s) BN_free(sig->s);
{ EVP_MD_CTX_destroy (ctx); } sig->r = r;
sig->s = s;
return 1;
}
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
*pr = sig->r;
*ps = sig->s;
}
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
if (r->n) BN_free(r->n);
if (r->e) BN_free(r->e);
if (r->d) BN_free(r->d);
r->n = n;
r->e = e;
r->d = d;
return 1;
}
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) {
*n = r->n;
*e = r->e;
*d = r->d;
}
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
if (dh->p) BN_free(dh->p);
if (dh->q) BN_free(dh->q);
if (dh->g) BN_free(dh->g);
dh->p = p;
dh->q = q;
dh->g = g;
return 1;
}
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) {
if (dh->pub_key) BN_free(dh->pub_key);
if (dh->priv_key) BN_free(dh->priv_key);
dh->pub_key = pub_key;
dh->priv_key = priv_key;
return 1;
}
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) {
*pub_key = dh->pub_key;
*priv_key = dh->priv_key;
}
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) { return pkey->pkey.rsa; }
inline EVP_MD_CTX *EVP_MD_CTX_new() { return EVP_MD_CTX_create(); }
inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); }
// ssl // ssl
#define TLS_method TLSv1_method #define TLS_method TLSv1_method

View file

@ -11,175 +11,154 @@
#include "Gost.h" #include "Gost.h"
#include "CryptoKey.h" #include "CryptoKey.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto ElGamalEncryptor::ElGamalEncryptor(const uint8_t *pub) {
{ memcpy(m_PublicKey, pub, 256);
ElGamalEncryptor::ElGamalEncryptor (const uint8_t * pub) }
{
memcpy (m_PublicKey, pub, 256);
}
void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) void ElGamalEncryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
{ ElGamalEncrypt(m_PublicKey, data, encrypted);
ElGamalEncrypt (m_PublicKey, data, encrypted); }
}
ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv) ElGamalDecryptor::ElGamalDecryptor(const uint8_t *priv) {
{ memcpy(m_PrivateKey, priv, 256);
memcpy (m_PrivateKey, priv, 256); }
}
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) bool ElGamalDecryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
{ return ElGamalDecrypt(m_PrivateKey, encrypted, data);
return ElGamalDecrypt (m_PrivateKey, encrypted, data); }
}
ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub) ECIESP256Encryptor::ECIESP256Encryptor(const uint8_t *pub) {
{ m_Curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
m_Curve = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); m_PublicKey = EC_POINT_new(m_Curve);
m_PublicKey = EC_POINT_new (m_Curve); BIGNUM *x = BN_bin2bn(pub, 32, nullptr);
BIGNUM * x = BN_bin2bn (pub, 32, nullptr); BIGNUM *y = BN_bin2bn(pub + 32, 32, nullptr);
BIGNUM * y = BN_bin2bn (pub + 32, 32, nullptr); if (!EC_POINT_set_affine_coordinates_GFp(m_Curve, m_PublicKey, x, y, nullptr))
if (!EC_POINT_set_affine_coordinates_GFp (m_Curve, m_PublicKey, x, y, nullptr)) LogPrint(eLogError, "ECICS P256 invalid public key");
LogPrint (eLogError, "ECICS P256 invalid public key"); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
} }
ECIESP256Encryptor::~ECIESP256Encryptor () ECIESP256Encryptor::~ECIESP256Encryptor() {
{ if (m_Curve) EC_GROUP_free(m_Curve);
if (m_Curve) EC_GROUP_free (m_Curve); if (m_PublicKey) EC_POINT_free(m_PublicKey);
if (m_PublicKey) EC_POINT_free (m_PublicKey); }
}
void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) void ECIESP256Encryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
{ if (m_Curve && m_PublicKey)
if (m_Curve && m_PublicKey) ECIESEncrypt(m_Curve, m_PublicKey, data, encrypted);
ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted); }
}
ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv) ECIESP256Decryptor::ECIESP256Decryptor(const uint8_t *priv) {
{ m_Curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
m_Curve = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); m_PrivateKey = BN_bin2bn(priv, 32, nullptr);
m_PrivateKey = BN_bin2bn (priv, 32, nullptr); }
}
ECIESP256Decryptor::~ECIESP256Decryptor () ECIESP256Decryptor::~ECIESP256Decryptor() {
{ if (m_Curve) EC_GROUP_free(m_Curve);
if (m_Curve) EC_GROUP_free (m_Curve); if (m_PrivateKey) BN_free(m_PrivateKey);
if (m_PrivateKey) BN_free (m_PrivateKey); }
}
bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) bool ECIESP256Decryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
{ if (m_Curve && m_PrivateKey)
if (m_Curve && m_PrivateKey) return ECIESDecrypt(m_Curve, m_PrivateKey, encrypted, data);
return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data); return false;
return false; }
}
void CreateECIESP256RandomKeys (uint8_t * priv, uint8_t * pub) void CreateECIESP256RandomKeys(uint8_t *priv, uint8_t *pub) {
{ EC_GROUP *curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_GROUP * curve = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); EC_POINT *p = nullptr;
EC_POINT * p = nullptr; BIGNUM *key = nullptr;
BIGNUM * key = nullptr; GenerateECIESKeyPair(curve, key, p);
GenerateECIESKeyPair (curve, key, p); bn2buf(key, priv, 32);
bn2buf (key, priv, 32); RAND_bytes(priv + 32, 224);
RAND_bytes (priv + 32, 224); BN_free(key);
BN_free (key); BIGNUM *x = BN_new(), *y = BN_new();
BIGNUM * x = BN_new (), * y = BN_new (); EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, NULL);
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, NULL); bn2buf(x, pub, 32);
bn2buf (x, pub, 32); bn2buf(y, pub + 32, 32);
bn2buf (y, pub + 32, 32); RAND_bytes(pub + 64, 192);
RAND_bytes (pub + 64, 192); EC_POINT_free(p);
EC_POINT_free (p); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
EC_GROUP_free (curve); EC_GROUP_free(curve);
} }
ECIESGOSTR3410Encryptor::ECIESGOSTR3410Encryptor (const uint8_t * pub) ECIESGOSTR3410Encryptor::ECIESGOSTR3410Encryptor(const uint8_t *pub) {
{ auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA);
auto& curve = GetGOSTR3410Curve (eGOSTR3410CryptoProA); m_PublicKey = EC_POINT_new(curve->GetGroup());
m_PublicKey = EC_POINT_new (curve->GetGroup ()); BIGNUM *x = BN_bin2bn(pub, 32, nullptr);
BIGNUM * x = BN_bin2bn (pub, 32, nullptr); BIGNUM *y = BN_bin2bn(pub + 32, 32, nullptr);
BIGNUM * y = BN_bin2bn (pub + 32, 32, nullptr); if (!EC_POINT_set_affine_coordinates_GFp(curve->GetGroup(), m_PublicKey, x, y, nullptr))
if (!EC_POINT_set_affine_coordinates_GFp (curve->GetGroup (), m_PublicKey, x, y, nullptr)) LogPrint(eLogError, "ECICS GOST R 34.10 invalid public key");
LogPrint (eLogError, "ECICS GOST R 34.10 invalid public key"); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
} }
ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor () ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor() {
{ if (m_PublicKey) EC_POINT_free(m_PublicKey);
if (m_PublicKey) EC_POINT_free (m_PublicKey); }
}
void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) void ECIESGOSTR3410Encryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
{ if (m_PublicKey)
if (m_PublicKey) ECIESEncrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PublicKey, data, encrypted);
ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted); }
}
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv) ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor(const uint8_t *priv) {
{ m_PrivateKey = BN_bin2bn(priv, 32, nullptr);
m_PrivateKey = BN_bin2bn (priv, 32, nullptr); }
}
ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor () ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor() {
{ if (m_PrivateKey) BN_free(m_PrivateKey);
if (m_PrivateKey) BN_free (m_PrivateKey); }
}
bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) bool ECIESGOSTR3410Decryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
{ if (m_PrivateKey)
if (m_PrivateKey) return ECIESDecrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PrivateKey, encrypted, data);
return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data); return false;
return false; }
}
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub) void CreateECIESGOSTR3410RandomKeys(uint8_t *priv, uint8_t *pub) {
{ auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA);
auto& curve = GetGOSTR3410Curve (eGOSTR3410CryptoProA); EC_POINT *p = nullptr;
EC_POINT * p = nullptr; BIGNUM *key = nullptr;
BIGNUM * key = nullptr; GenerateECIESKeyPair(curve->GetGroup(), key, p);
GenerateECIESKeyPair (curve->GetGroup (), key, p); bn2buf(key, priv, 32);
bn2buf (key, priv, 32); RAND_bytes(priv + 32, 224);
RAND_bytes (priv + 32, 224); BN_free(key);
BN_free (key); BIGNUM *x = BN_new(), *y = BN_new();
BIGNUM * x = BN_new (), * y = BN_new (); EC_POINT_get_affine_coordinates_GFp(curve->GetGroup(), p, x, y, NULL);
EC_POINT_get_affine_coordinates_GFp (curve->GetGroup (), p, x, y, NULL); bn2buf(x, pub, 32);
bn2buf (x, pub, 32); bn2buf(y, pub + 32, 32);
bn2buf (y, pub + 32, 32); RAND_bytes(pub + 64, 192);
RAND_bytes (pub + 64, 192); EC_POINT_free(p);
EC_POINT_free (p); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
} }
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub) ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor(const uint8_t *pub) {
{ memcpy(m_PublicKey, pub, 32);
memcpy (m_PublicKey, pub, 32); }
}
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub) void ECIESX25519AEADRatchetEncryptor::Encrypt(const uint8_t *, uint8_t *pub) {
{ memcpy(pub, m_PublicKey, 32);
memcpy (pub, m_PublicKey, 32); }
}
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic) ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic) {
{ m_StaticKeys.SetPrivateKey(priv, calculatePublic);
m_StaticKeys.SetPrivateKey (priv, calculatePublic); }
}
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret) bool ECIESX25519AEADRatchetDecryptor::Decrypt(const uint8_t *epub, uint8_t *sharedSecret) {
{ return m_StaticKeys.Agree(epub, sharedSecret);
return m_StaticKeys.Agree (epub, sharedSecret); }
}
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub) void CreateECIESX25519AEADRatchetRandomKeys(uint8_t *priv, uint8_t *pub) {
{ X25519Keys k;
X25519Keys k; k.GenerateKeys();
k.GenerateKeys (); k.GetPrivateKey(priv);
k.GetPrivateKey (priv); memcpy(pub, k.GetPublicKey(), 32);
memcpy (pub, k.GetPublicKey (), 32); }
} }
}
} }

View file

@ -12,153 +12,164 @@
#include <inttypes.h> #include <inttypes.h>
#include "Crypto.h" #include "Crypto.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto class CryptoKeyEncryptor {
{ public:
class CryptoKeyEncryptor
{
public:
virtual ~CryptoKeyEncryptor () {}; virtual ~CryptoKeyEncryptor() {};
virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) = 0;
};
class CryptoKeyDecryptor virtual void Encrypt(const uint8_t *data, uint8_t *encrypted) = 0;
{ };
public:
virtual ~CryptoKeyDecryptor () {}; class CryptoKeyDecryptor {
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data) = 0; public:
virtual size_t GetPublicKeyLen () const = 0; // we need it to set key in LS2
}; virtual ~CryptoKeyDecryptor() {};
virtual bool Decrypt(const uint8_t *encrypted, uint8_t *data) = 0;
virtual size_t GetPublicKeyLen() const = 0; // we need it to set key in LS2
};
// ElGamal // ElGamal
class ElGamalEncryptor: public CryptoKeyEncryptor // for destination class ElGamalEncryptor : public CryptoKeyEncryptor // for destination
{ {
public: public:
ElGamalEncryptor (const uint8_t * pub); ElGamalEncryptor(const uint8_t *pub);
void Encrypt (const uint8_t * data, uint8_t * encrypted) override; // 222 bytes data, 514 bytes encrypted
private: void Encrypt(const uint8_t *data, uint8_t *encrypted) override; // 222 bytes data, 514 bytes encrypted
uint8_t m_PublicKey[256]; private:
};
class ElGamalDecryptor: public CryptoKeyDecryptor // for destination uint8_t m_PublicKey[256];
{ };
public:
ElGamalDecryptor (const uint8_t * priv); class ElGamalDecryptor : public CryptoKeyDecryptor // for destination
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; // 514 bytes encrypted, 222 bytes data {
size_t GetPublicKeyLen () const override { return 256; }; public:
private: ElGamalDecryptor(const uint8_t *priv);
uint8_t m_PrivateKey[256]; bool Decrypt(const uint8_t *encrypted, uint8_t *data) override; // 514 bytes encrypted, 222 bytes data
}; size_t GetPublicKeyLen() const override { return 256; };
private:
uint8_t m_PrivateKey[256];
};
// ECIES P256 // ECIES P256
class ECIESP256Encryptor: public CryptoKeyEncryptor class ECIESP256Encryptor : public CryptoKeyEncryptor {
{ public:
public:
ECIESP256Encryptor (const uint8_t * pub); ECIESP256Encryptor(const uint8_t *pub);
~ECIESP256Encryptor ();
void Encrypt (const uint8_t * data, uint8_t * encrypted) override;
private: ~ECIESP256Encryptor();
EC_GROUP * m_Curve; void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
EC_POINT * m_PublicKey;
}; private:
EC_GROUP *m_Curve;
EC_POINT *m_PublicKey;
};
class ECIESP256Decryptor: public CryptoKeyDecryptor class ECIESP256Decryptor : public CryptoKeyDecryptor {
{ public:
public:
ECIESP256Decryptor (const uint8_t * priv); ECIESP256Decryptor(const uint8_t *priv);
~ECIESP256Decryptor ();
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override;
size_t GetPublicKeyLen () const override { return 64; };
private: ~ECIESP256Decryptor();
EC_GROUP * m_Curve; bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
BIGNUM * m_PrivateKey;
};
void CreateECIESP256RandomKeys (uint8_t * priv, uint8_t * pub); size_t GetPublicKeyLen() const override { return 64; };
private:
EC_GROUP *m_Curve;
BIGNUM *m_PrivateKey;
};
void CreateECIESP256RandomKeys(uint8_t *priv, uint8_t *pub);
// ECIES GOST R 34.10 // ECIES GOST R 34.10
class ECIESGOSTR3410Encryptor: public CryptoKeyEncryptor class ECIESGOSTR3410Encryptor : public CryptoKeyEncryptor {
{ public:
public:
ECIESGOSTR3410Encryptor (const uint8_t * pub); ECIESGOSTR3410Encryptor(const uint8_t *pub);
~ECIESGOSTR3410Encryptor ();
void Encrypt (const uint8_t * data, uint8_t * encrypted) override;
private: ~ECIESGOSTR3410Encryptor();
EC_POINT * m_PublicKey; void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
};
private:
EC_POINT *m_PublicKey;
};
class ECIESGOSTR3410Decryptor: public CryptoKeyDecryptor class ECIESGOSTR3410Decryptor : public CryptoKeyDecryptor {
{ public:
public:
ECIESGOSTR3410Decryptor (const uint8_t * priv); ECIESGOSTR3410Decryptor(const uint8_t *priv);
~ECIESGOSTR3410Decryptor ();
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override;
size_t GetPublicKeyLen () const override { return 64; };
private: ~ECIESGOSTR3410Decryptor();
BIGNUM * m_PrivateKey; bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
};
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub); size_t GetPublicKeyLen() const override { return 64; };
private:
BIGNUM *m_PrivateKey;
};
void CreateECIESGOSTR3410RandomKeys(uint8_t *priv, uint8_t *pub);
// ECIES-X25519-AEAD-Ratchet // ECIES-X25519-AEAD-Ratchet
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor class ECIESX25519AEADRatchetEncryptor : public CryptoKeyEncryptor {
{ public:
public:
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); ECIESX25519AEADRatchetEncryptor(const uint8_t *pub);
~ECIESX25519AEADRatchetEncryptor () {};
void Encrypt (const uint8_t *, uint8_t * pub) override;
// copies m_PublicKey to pub
private: ~ECIESX25519AEADRatchetEncryptor() {};
uint8_t m_PublicKey[32]; void Encrypt(const uint8_t *, uint8_t *pub) override;
}; // copies m_PublicKey to pub
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor private:
{
public:
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false); uint8_t m_PublicKey[32];
~ECIESX25519AEADRatchetDecryptor () {}; };
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret) override;
// agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const override { return 32; };
const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); };
private: class ECIESX25519AEADRatchetDecryptor : public CryptoKeyDecryptor {
public:
X25519Keys m_StaticKeys; ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic = false);
};
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); ~ECIESX25519AEADRatchetDecryptor() {};
}
bool Decrypt(const uint8_t *epub, uint8_t *sharedSecret) override;
// agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen() const override { return 32; };
const uint8_t *GetPubicKey() const { return m_StaticKeys.GetPublicKey(); };
private:
X25519Keys m_StaticKeys;
};
void CreateECIESX25519AEADRatchetRandomKeys(uint8_t *priv, uint8_t *pub);
}
} }
#endif #endif

View file

@ -14,420 +14,375 @@
#include "Destination.h" #include "Destination.h"
#include "Datagram.h" #include "Datagram.h"
namespace i2p namespace i2p {
{ namespace datagram {
namespace datagram DatagramDestination::DatagramDestination(std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip) :
{ m_Owner(owner), m_Receiver(nullptr), m_RawReceiver(nullptr), m_Gzip(gzip) {
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip): if (m_Gzip)
m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip) m_Deflator.reset(new i2p::data::GzipDeflator);
{
if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator);
auto identityLen = m_Owner->GetIdentity ()->GetFullLen (); auto identityLen = m_Owner->GetIdentity()->GetFullLen();
m_From.resize (identityLen); m_From.resize(identityLen);
m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen); m_Owner->GetIdentity()->ToBuffer(m_From.data(), identityLen);
m_Signature.resize (m_Owner->GetIdentity ()->GetSignatureLen ()); m_Signature.resize(m_Owner->GetIdentity()->GetSignatureLen());
} }
DatagramDestination::~DatagramDestination () DatagramDestination::~DatagramDestination() {
{ m_Sessions.clear();
m_Sessions.clear(); }
}
void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) void
{ DatagramDestination::SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &identity,
auto session = ObtainSession(identity); uint16_t fromPort, uint16_t toPort) {
SendDatagram (session, payload, len, fromPort, toPort); auto session = ObtainSession(identity);
FlushSendQueue (session); SendDatagram(session, payload, len, fromPort, toPort);
} FlushSendQueue(session);
}
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) void
{ DatagramDestination::SendRawDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &identity,
auto session = ObtainSession(identity); uint16_t fromPort, uint16_t toPort) {
SendRawDatagram (session, payload, len, fromPort, toPort); auto session = ObtainSession(identity);
FlushSendQueue (session); SendRawDatagram(session, payload, len, fromPort, toPort);
} FlushSendQueue(session);
}
std::shared_ptr<DatagramSession> DatagramDestination::GetSession(const i2p::data::IdentHash & ident) std::shared_ptr<DatagramSession> DatagramDestination::GetSession(const i2p::data::IdentHash &ident) {
{ return ObtainSession(ident);
return ObtainSession(ident); }
}
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) void
{ DatagramDestination::SendDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
if (session) uint16_t fromPort, uint16_t toPort) {
{ if (session) {
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) if (m_Owner->GetIdentity()->GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) {
{ uint8_t hash[32];
uint8_t hash[32]; SHA256(payload, len, hash);
SHA256(payload, len, hash); m_Owner->Sign(hash, 32, m_Signature.data());
m_Owner->Sign (hash, 32, m_Signature.data ()); } else
} m_Owner->Sign(payload, len, m_Signature.data());
else
m_Owner->Sign (payload, len, m_Signature.data ());
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}}, auto msg = CreateDataMessage({{m_From.data(), m_From.size()},
fromPort, toPort, false, !session->IsRatchets ()); // datagram {m_Signature.data(), m_Signature.size()},
session->SendMsg(msg); {payload, len}},
} fromPort, toPort, false, !session->IsRatchets()); // datagram
} session->SendMsg(msg);
}
}
void DatagramDestination::SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) void DatagramDestination::SendRawDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload,
{ size_t len, uint16_t fromPort, uint16_t toPort) {
if (session) if (session)
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ())); // raw session->SendMsg(
} CreateDataMessage({{payload, len}}, fromPort, toPort, true, !session->IsRatchets())); // raw
}
void DatagramDestination::FlushSendQueue (std::shared_ptr<DatagramSession> session) void DatagramDestination::FlushSendQueue(std::shared_ptr<DatagramSession> session) {
{ if (session)
if (session) session->FlushSendQueue();
session->FlushSendQueue (); }
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len) void DatagramDestination::HandleDatagram(uint16_t fromPort, uint16_t toPort, uint8_t *const &buf, size_t len) {
{ i2p::data::IdentityEx identity;
i2p::data::IdentityEx identity; size_t identityLen = identity.FromBuffer(buf, len);
size_t identityLen = identity.FromBuffer (buf, len); const uint8_t *signature = buf + identityLen;
const uint8_t * signature = buf + identityLen; size_t headerLen = identityLen + identity.GetSignatureLen();
size_t headerLen = identityLen + identity.GetSignatureLen ();
bool verified = false; bool verified = false;
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) if (identity.GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) {
{ uint8_t hash[32];
uint8_t hash[32]; SHA256(buf + headerLen, len - headerLen, hash);
SHA256(buf + headerLen, len - headerLen, hash); verified = identity.Verify(hash, 32, signature);
verified = identity.Verify (hash, 32, signature); } else
} verified = identity.Verify(buf + headerLen, len - headerLen, signature);
else
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
if (verified) if (verified) {
{ auto h = identity.GetIdentHash();
auto h = identity.GetIdentHash(); auto session = ObtainSession(h);
auto session = ObtainSession(h); session->Ack();
session->Ack(); auto r = FindReceiver(toPort);
auto r = FindReceiver(toPort); if (r)
if(r) r(identity, fromPort, toPort, buf + headerLen, len - headerLen);
r(identity, fromPort, toPort, buf + headerLen, len -headerLen); else
else LogPrint(eLogWarning, "DatagramDestination: no receiver for port ", toPort);
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort); } else
} LogPrint(eLogWarning, "Datagram signature verification failed");
else }
LogPrint (eLogWarning, "Datagram signature verification failed");
}
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void
{ DatagramDestination::HandleRawDatagram(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len) {
if (m_RawReceiver) if (m_RawReceiver)
m_RawReceiver (fromPort, toPort, buf, len); m_RawReceiver(fromPort, toPort, buf, len);
else else
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram"); LogPrint(eLogWarning, "DatagramDestination: no receiver for raw datagram");
} }
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) {
{ std::lock_guard<std::mutex> lock(m_ReceiversMutex);
std::lock_guard<std::mutex> lock(m_ReceiversMutex); Receiver r = m_Receiver;
Receiver r = m_Receiver; auto itr = m_ReceiversByPorts.find(port);
auto itr = m_ReceiversByPorts.find(port); if (itr != m_ReceiversByPorts.end())
if (itr != m_ReceiversByPorts.end()) r = itr->second;
r = itr->second; return r;
return r; }
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw) void DatagramDestination::HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf,
{ size_t len, bool isRaw) {
// unzip it // unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE]; uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE); size_t uncompressedLen = m_Inflator.Inflate(buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen) if (uncompressedLen) {
{ if (isRaw)
if (isRaw) HandleRawDatagram(fromPort, toPort, uncompressed, uncompressedLen);
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen); else
else HandleDatagram(fromPort, toPort, uncompressed, uncompressedLen);
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); } else
} LogPrint(eLogWarning, "Datagram: decompression failed");
else }
LogPrint (eLogWarning, "Datagram: decompression failed");
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage ( std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage(
const std::vector<std::pair<const uint8_t *, size_t> >& payloads, const std::vector<std::pair<const uint8_t *, size_t> > &payloads,
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) {
{ size_t size;
size_t size; auto msg = m_I2NPMsgsPool.AcquireShared();
auto msg = m_I2NPMsgsPool.AcquireShared (); uint8_t *buf = msg->GetPayload();
uint8_t * buf = msg->GetPayload (); buf += 4; // reserve for length
buf += 4; // reserve for length
if (m_Gzip && m_Deflator) if (m_Gzip && m_Deflator)
size = m_Deflator->Deflate (payloads, buf, msg->maxLen - msg->len); size = m_Deflator->Deflate(payloads, buf, msg->maxLen - msg->len);
else else
size = i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); size = i2p::data::GzipNoCompression(payloads, buf, msg->maxLen - msg->len);
if (size) if (size) {
{ htobe32buf(msg->GetPayload(), size); // length
htobe32buf (msg->GetPayload (), size); // length htobe16buf(buf + 4, fromPort); // source port
htobe16buf (buf + 4, fromPort); // source port htobe16buf(buf + 6, toPort); // destination port
htobe16buf (buf + 6, toPort); // destination port buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
msg->len += size + 4; msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); msg->FillI2NPMessageHeader(eI2NPData, 0, checksum);
} } else
else msg = nullptr;
msg = nullptr; return msg;
return msg; }
}
void DatagramDestination::CleanUp () void DatagramDestination::CleanUp() {
{ if (m_Sessions.empty()) return;
if (m_Sessions.empty ()) return; auto now = i2p::util::GetMillisecondsSinceEpoch();
auto now = i2p::util::GetMillisecondsSinceEpoch(); LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
LogPrint(eLogDebug, "DatagramDestination: clean up sessions"); std::unique_lock<std::mutex> lock(m_SessionsMutex);
std::unique_lock<std::mutex> lock(m_SessionsMutex); // for each session ...
// for each session ... for (auto it = m_Sessions.begin(); it != m_Sessions.end();) {
for (auto it = m_Sessions.begin (); it != m_Sessions.end (); ) // check if expired
{ if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE) {
// check if expired LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE) it->second->Stop();
{ it = m_Sessions.erase(it); // we are expired
LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32()); } else
it->second->Stop (); it++;
it = m_Sessions.erase (it); // we are expired }
} }
else
it++;
}
}
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity) std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash &identity) {
{ std::shared_ptr<DatagramSession> session = nullptr;
std::shared_ptr<DatagramSession> session = nullptr; std::lock_guard<std::mutex> lock(m_SessionsMutex);
std::lock_guard<std::mutex> lock(m_SessionsMutex); auto itr = m_Sessions.find(identity);
auto itr = m_Sessions.find(identity); if (itr == m_Sessions.end()) {
if (itr == m_Sessions.end()) { // not found, create new session
// not found, create new session session = std::make_shared<DatagramSession>(m_Owner, identity);
session = std::make_shared<DatagramSession>(m_Owner, identity); session->Start();
session->Start (); m_Sessions[identity] = session;
m_Sessions[identity] = session; } else {
} else { session = itr->second;
session = itr->second; }
} return session;
return session; }
}
std::shared_ptr<DatagramSession::Info> DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote) std::shared_ptr<DatagramSession::Info>
{ DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash &remote) {
std::lock_guard<std::mutex> lock(m_SessionsMutex); std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( auto & item : m_Sessions) for (auto &item: m_Sessions) {
{ if (item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo()); }
} return nullptr;
return nullptr; }
}
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) : const i2p::data::IdentHash &remoteIdent) :
m_LocalDestination(localDestination), m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent), m_RemoteIdent(remoteIdent),
m_RequestingLS(false) m_RequestingLS(false) {
{ }
}
void DatagramSession::Start () void DatagramSession::Start() {
{ m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
m_LastUse = i2p::util::GetMillisecondsSinceEpoch (); }
}
void DatagramSession::Stop () void DatagramSession::Stop() {
{ }
}
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg) void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg) {
{ // we used this session
// we used this session m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); if (msg || m_SendQueue.empty())
if (msg || m_SendQueue.empty ()) m_SendQueue.push_back(msg);
m_SendQueue.push_back(msg); // flush queue right away if full
// flush queue right away if full if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue();
FlushSendQueue(); }
}
DatagramSession::Info DatagramSession::GetSessionInfo() const DatagramSession::Info DatagramSession::GetSessionInfo() const {
{ if (!m_RoutingSession)
if(!m_RoutingSession) return DatagramSession::Info(nullptr, nullptr, m_LastUse);
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto routingPath = m_RoutingSession->GetSharedRoutingPath(); auto routingPath = m_RoutingSession->GetSharedRoutingPath();
if (!routingPath) if (!routingPath)
return DatagramSession::Info(nullptr, nullptr, m_LastUse); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto lease = routingPath->remoteLease; auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel; auto tunnel = routingPath->outboundTunnel;
if(lease) if (lease) {
{ if (tunnel)
if(tunnel) return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse); else
else return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse); } else if (tunnel)
} return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
else if(tunnel) else
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
else }
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
}
void DatagramSession::Ack() void DatagramSession::Ack() {
{ m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); auto path = GetSharedRoutingPath();
auto path = GetSharedRoutingPath(); if (path)
if(path) path->updateTime = i2p::util::GetSecondsSinceEpoch();
path->updateTime = i2p::util::GetSecondsSinceEpoch (); if (IsRatchets())
if (IsRatchets ()) SendMsg(nullptr); // send empty message in case if we have some data to send
SendMsg (nullptr); // send empty message in case if we have some data to send }
}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath () std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath() {
{ if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired()) {
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
{ if (!m_RemoteLeaseSet) {
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); if (!m_RequestingLS) {
if (!m_RemoteLeaseSet) m_RequestingLS = true;
{ m_LocalDestination->RequestDestination(m_RemoteIdent,
if(!m_RequestingLS) std::bind(&DatagramSession::HandleLeaseSetUpdated, this,
{ std::placeholders::_1));
m_RequestingLS = true; }
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); return nullptr;
} }
return nullptr; }
}
}
if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) if (!m_RoutingSession || m_RoutingSession->IsTerminated() || !m_RoutingSession->IsReadyToSend()) {
{ bool found = false;
bool found = false; for (auto &it: m_PendingRoutingSessions)
for (auto& it: m_PendingRoutingSessions) if (it->GetOwner() && m_RoutingSession->IsReadyToSend()) // found established session
if (it->GetOwner () && m_RoutingSession->IsReadyToSend ()) // found established session {
{ m_RoutingSession = it;
m_RoutingSession = it; m_PendingRoutingSessions.clear();
m_PendingRoutingSessions.clear (); found = true;
found = true; break;
break; }
} if (!found) {
if (!found) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
{ if (!m_RoutingSession->GetOwner() || !m_RoutingSession->IsReadyToSend())
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); m_PendingRoutingSessions.push_back(m_RoutingSession);
if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) }
m_PendingRoutingSessions.push_back (m_RoutingSession); }
}
}
auto path = m_RoutingSession->GetSharedRoutingPath(); auto path = m_RoutingSession->GetSharedRoutingPath();
if (path && m_RoutingSession->IsRatchets () && if (path && m_RoutingSession->IsRatchets() &&
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT) m_LastUse > m_RoutingSession->GetLastActivityTimestamp() * 1000 + DATAGRAM_SESSION_PATH_TIMEOUT) {
{ m_RoutingSession->SetSharedRoutingPath(nullptr);
m_RoutingSession->SetSharedRoutingPath (nullptr); path = nullptr;
path = nullptr; }
}
if (path) if (path) {
{ if (path->outboundTunnel && !path->outboundTunnel->IsEstablished()) {
if (path->outboundTunnel && !path->outboundTunnel->IsEstablished ()) // bad outbound tunnel, switch outbound tunnel
{ path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(
// bad outbound tunnel, switch outbound tunnel path->outboundTunnel);
path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(path->outboundTunnel); if (!path->outboundTunnel)
if (!path->outboundTunnel) m_RoutingSession->SetSharedRoutingPath(nullptr);
m_RoutingSession->SetSharedRoutingPath (nullptr); }
}
if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
{ // bad lease, switch to next one
// bad lease, switch to next one if (m_RemoteLeaseSet) {
if (m_RemoteLeaseSet) auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding(
{ [&](const i2p::data::Lease &l) -> bool {
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( return l.tunnelID == path->remoteLease->tunnelID;
[&](const i2p::data::Lease& l) -> bool });
{ auto sz = ls.size();
return l.tunnelID == path->remoteLease->tunnelID; if (sz) {
}); auto idx = rand() % sz;
auto sz = ls.size(); path->remoteLease = ls[idx];
if (sz) } else
{ m_RoutingSession->SetSharedRoutingPath(nullptr);
auto idx = rand() % sz; } else {
path->remoteLease = ls[idx]; // no remote lease set?
} LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ",
else m_RemoteIdent.ToBase32());
m_RoutingSession->SetSharedRoutingPath (nullptr); m_RoutingSession->SetSharedRoutingPath(nullptr);
} }
else }
{ } else {
// no remote lease set? // no current path, make one
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32()); path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
m_RoutingSession->SetSharedRoutingPath (nullptr);
}
}
}
else
{
// no current path, make one
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
if (m_RemoteLeaseSet) if (m_RemoteLeaseSet) {
{ // pick random next good lease
// pick random next good lease auto ls = m_RemoteLeaseSet->GetNonExpiredLeases();
auto ls = m_RemoteLeaseSet->GetNonExpiredLeases(); auto sz = ls.size();
auto sz = ls.size(); if (sz) {
if (sz) auto idx = rand() % sz;
{ path->remoteLease = ls[idx];
auto idx = rand() % sz; } else
path->remoteLease = ls[idx]; return nullptr;
}
else
return nullptr;
auto leaseRouter = i2p::data::netdb.FindRouter (path->remoteLease->tunnelGateway); auto leaseRouter = i2p::data::netdb.FindRouter(path->remoteLease->tunnelGateway);
path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(nullptr, path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(nullptr,
leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); leaseRouter
if (!path->outboundTunnel) return nullptr; ? leaseRouter->GetCompatibleTransports(
} false)
else : (i2p::data::RouterInfo::CompatibleTransports) i2p::data::RouterInfo::eAllTransports);
{ if (!path->outboundTunnel) return nullptr;
// no remote lease set currently, bail } else {
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32()); // no remote lease set currently, bail
return nullptr; LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
} return nullptr;
m_RoutingSession->SetSharedRoutingPath(path); }
} m_RoutingSession->SetSharedRoutingPath(path);
return path; }
} return path;
}
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls) void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls) {
{ m_RequestingLS = false;
m_RequestingLS = false; if (!ls) return;
if(!ls) return; // only update lease set if found and newer than previous lease set
// only update lease set if found and newer than previous lease set uint64_t oldExpire = 0;
uint64_t oldExpire = 0; if (m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); if (ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; }
}
void DatagramSession::FlushSendQueue () void DatagramSession::FlushSendQueue() {
{ if (m_SendQueue.empty()) return;
if (m_SendQueue.empty ()) return; std::vector<i2p::tunnel::TunnelMessageBlock> send;
std::vector<i2p::tunnel::TunnelMessageBlock> send; auto routingPath = GetSharedRoutingPath();
auto routingPath = GetSharedRoutingPath(); // if we don't have a routing path we will drop all queued messages
// if we don't have a routing path we will drop all queued messages if (routingPath && routingPath->outboundTunnel && routingPath->remoteLease) {
if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease) for (const auto &msg: m_SendQueue) {
{ auto m = m_RoutingSession->WrapSingleMessage(msg);
for (const auto & msg : m_SendQueue) if (m)
{ send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,
auto m = m_RoutingSession->WrapSingleMessage(msg); routingPath->remoteLease->tunnelGateway,
if (m) routingPath->remoteLease->tunnelID, m});
send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); }
} routingPath->outboundTunnel->SendTunnelDataMsg(send);
routingPath->outboundTunnel->SendTunnelDataMsg(send); }
} m_SendQueue.clear();
m_SendQueue.clear(); }
} }
}
} }

View file

@ -20,155 +20,184 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p {
{ namespace client {
namespace client class ClientDestination;
{ }
class ClientDestination; namespace datagram {
} // milliseconds for max session idle time
namespace datagram const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
{ // milliseconds for how long we try sticking to a dead routing path before trying to switch
// milliseconds for max session idle time const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000;
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000; // milliseconds interval a routing path is used before switching
// milliseconds for how long we try sticking to a dead routing path before trying to switch const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000; // milliseconds before lease expire should we try switching leases
// milliseconds interval a routing path is used before switching const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 30 * 1000;
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000; // milliseconds fudge factor for leases handover
// milliseconds before lease expire should we try switching leases const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 30 * 1000; // milliseconds minimum time between path switches
// milliseconds fudge factor for leases handover const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; // max 64 messages buffered in send queue for each datagram session
// milliseconds minimum time between path switches const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
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;
class DatagramSession : public std::enable_shared_from_this<DatagramSession> class DatagramSession : public std::enable_shared_from_this<DatagramSession> {
{
public: public:
DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, const i2p::data::IdentHash & remoteIdent); DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash &remoteIdent);
void Start (); void Start();
void Stop ();
void Stop();
/** @brief ack the garlic routing path */ /** @brief ack the garlic routing path */
void Ack(); void Ack();
/** send an i2np message to remote endpoint for this session */ /** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg); void SendMsg(std::shared_ptr<I2NPMessage> msg);
void FlushSendQueue();
/** 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 (); } void FlushSendQueue();
struct Info /** get the last time in milliseconds for when we used this datagram session */
{ uint64_t LastActivity() const { return m_LastUse; }
std::shared_ptr<const i2p::data::IdentHash> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity;
Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} bool IsRatchets() const { return m_RoutingSession && m_RoutingSession->IsRatchets(); }
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) :
activity(a) {
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
else IBGW = nullptr;
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
else OBEP = nullptr;
}
};
Info GetSessionInfo() const; struct Info {
std::shared_ptr<const i2p::data::IdentHash> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity;
private: Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath(); Info(const uint8_t *ibgw, const uint8_t *obep, const uint64_t a) :
activity(a) {
if (ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
else IBGW = nullptr;
if (obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
else OBEP = nullptr;
}
};
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls); Info GetSessionInfo() const;
private: private:
std::shared_ptr<i2p::client::ClientDestination> m_LocalDestination; std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath();
i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastUse;
bool m_RequestingLS;
};
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr; void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
const size_t MAX_DATAGRAM_SIZE = 32768; private:
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
public: std::shared_ptr<i2p::client::ClientDestination> m_LocalDestination;
i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastUse;
bool m_RequestingLS;
};
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip); typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); const size_t MAX_DATAGRAM_SIZE = 32768;
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
// TODO: implement calls from other thread from SAM
std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash & ident); class DatagramDestination {
void SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); typedef std::function<void(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort,
void SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); const uint8_t *buf, size_t len)> Receiver;
void FlushSendQueue (std::shared_ptr<DatagramSession> session); typedef std::function<
void (uint16_t
fromPort,
uint16_t toPort,
const uint8_t *buf, size_t
len)>
RawReceiver;
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false); public:
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; DatagramDestination(std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip);
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; }; ~DatagramDestination();
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; }; void
void ResetRawReceiver () { m_RawReceiver = nullptr; }; SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident, uint16_t fromPort = 0,
uint16_t toPort = 0);
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote); void SendRawDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident,
uint16_t fromPort = 0, uint16_t toPort = 0);
// TODO: implement calls from other thread from SAM
// clean up stale sessions std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash &ident);
void CleanUp ();
private: void SendDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort);
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident); void SendRawDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort);
std::shared_ptr<I2NPMessage> CreateDataMessage (const std::vector<std::pair<const uint8_t *, size_t> >& payloads, void FlushSendQueue(std::shared_ptr<DatagramSession> session);
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 HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len,
void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); bool isRaw = false);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */ void SetReceiver(const Receiver &receiver) { m_Receiver = receiver; };
Receiver FindReceiver(uint16_t port);
private: void ResetReceiver() { m_Receiver = nullptr; };
std::shared_ptr<i2p::client::ClientDestination> m_Owner; void SetReceiver(const Receiver &receiver, uint16_t port) {
Receiver m_Receiver; // default std::lock_guard<std::mutex> lock(m_ReceiversMutex);
RawReceiver m_RawReceiver; // default m_ReceiversByPorts[port] = receiver;
bool m_Gzip; // gzip compression of data messages };
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
std::mutex m_ReceiversMutex;
std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator; void ResetReceiver(uint16_t port) {
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator; std::lock_guard<std::mutex> lock(m_ReceiversMutex);
std::vector<uint8_t> m_From, m_Signature; m_ReceiversByPorts.erase(port);
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool; };
};
} void SetRawReceiver(const RawReceiver &receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver() { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash &remote);
// clean up stale sessions
void CleanUp();
private:
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash &ident);
std::shared_ptr<I2NPMessage>
CreateDataMessage(const std::vector<std::pair<const uint8_t *, size_t> > &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);
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default
RawReceiver m_RawReceiver; // default
bool m_Gzip; // gzip compression of data messages
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr> m_Sessions;
std::mutex m_ReceiversMutex;
std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator;
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
std::vector<uint8_t> m_From, m_Signature;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -28,296 +28,378 @@
#include "Datagram.h" #include "Datagram.h"
#include "util.h" #include "util.h"
namespace i2p namespace i2p {
{ namespace client {
namespace client const uint8_t PROTOCOL_TYPE_STREAMING = 6;
{ const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_STREAMING = 6; const uint8_t PROTOCOL_TYPE_RAW = 18;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const uint8_t PROTOCOL_TYPE_RAW = 18; const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int PUBLISH_MIN_INTERVAL = 20; // in seconds
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
const int PUBLISH_MIN_INTERVAL = 20; // in seconds const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
// I2CP // I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity"; const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5; const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity"; const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5; const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance"; const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance";
const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0; const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0;
const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance"; const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance";
const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0; const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0;
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40; const int DEFAULT_TAGS_TO_SEND = 40;
const char I2CP_PARAM_RATCHET_INBOUND_TAGS[] = "crypto.ratchet.inboundTags"; const char I2CP_PARAM_RATCHET_INBOUND_TAGS[] = "crypto.ratchet.inboundTags";
const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet
const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname"; const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname";
const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname"; const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname";
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet"; const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType"; const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
const int DEFAULT_LEASESET_TYPE = 3; const int DEFAULT_LEASESET_TYPE = 3;
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; 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_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; 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_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 const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
// latency // latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
const int DEFAULT_MIN_TUNNEL_LATENCY = 0; const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max"; const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max";
const int DEFAULT_MAX_TUNNEL_LATENCY = 0; const int DEFAULT_MAX_TUNNEL_LATENCY = 0;
// streaming // streaming
const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay"; const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay";
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true; const int DEFAULT_ANSWER_PINGS = true;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void(std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class LeaseSetDestination: public i2p::garlic::GarlicDestination, class LeaseSetDestination : public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<LeaseSetDestination> public std::enable_shared_from_this<LeaseSetDestination> {
{ typedef std::function<void(std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
struct LeaseSetRequest
{
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls) // leaseSet = nullptr means not found
{ struct LeaseSetRequest {
for (auto& it: requestComplete) it (ls); LeaseSetRequest(boost::asio::io_service &service) : requestTime(0), requestTimeoutTimer(service) {};
requestComplete.clear (); std::set<i2p::data::IdentHash> excluded;
} uint64_t requestTime;
}; boost::asio::deadline_timer requestTimeoutTimer;
std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
public: void Complete(std::shared_ptr<i2p::data::LeaseSet> ls) {
for (auto &it: requestComplete) it(ls);
requestComplete.clear();
}
};
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr); public:
~LeaseSetDestination ();
const std::string& GetNickname () const { return m_Nickname; };
boost::asio::io_service& GetService () { return m_Service; };
virtual void Start (); LeaseSetDestination(boost::asio::io_service &service, bool isPublic,
virtual void Stop (); const std::map<std::string, std::string> *params = nullptr);
/** i2cp reconfigure */ ~LeaseSetDestination();
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; }; const std::string &GetNickname() const { return m_Nickname; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
// implements GarlicDestination boost::asio::io_service &GetService() { return m_Service; };
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
// override GarlicDestination virtual void Start();
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
bool IsPublic () const { return m_IsPublic; }; virtual void Stop();
void SetPublic (bool pub) { m_IsPublic = pub; };
protected: /** i2cp reconfigure */
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
// implements GarlicDestination std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() { return m_Pool; };
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet); bool IsReady() const {
int GetLeaseSetType () const { return m_LeaseSetType; }; return m_LeaseSet && !m_LeaseSet->IsExpired() && m_Pool->GetOutboundTunnels().size() > 0;
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; };
int GetAuthType () const { return m_AuthType; };
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
private: std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet(const i2p::data::IdentHash &ident);
void UpdateLeaseSet (); bool RequestDestination(const i2p::data::IdentHash &dest, RequestComplete requestComplete = nullptr);
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr); bool RequestDestinationWithEncryptedLeaseSet(std::shared_ptr<const i2p::data::BlindedPublicKey> dest,
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request); RequestComplete requestComplete = nullptr);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets ();
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
private: void CancelDestinationRequest(const i2p::data::IdentHash &dest, bool notify = true);
boost::asio::io_service& m_Service; void CancelDestinationRequestWithEncryptedLeaseSet(std::shared_ptr<const i2p::data::BlindedPublicKey> dest,
mutable std::mutex m_RemoteLeaseSetsMutex; bool notify = true);
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool; // implements GarlicDestination
std::mutex m_LeaseSetMutex; std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet();
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const { return m_Pool; }
m_PublishDelayTimer, m_CleanupTimer;
std::string m_Nickname;
int m_LeaseSetType, m_AuthType;
std::unique_ptr<i2p::data::Tag<32> > m_LeaseSetPrivKey; // non-null if presented
public: // override GarlicDestination
bool SubmitSessionKey(const uint8_t *key, const uint8_t *tag);
// for HTTP only void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag);
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; };
bool IsPerClientAuth () const { return m_AuthType > 0; };
};
class ClientDestination: public LeaseSetDestination void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
{
struct EncryptionKey
{
uint8_t pub[256], priv[256];
i2p::data::CryptoKeyType keyType;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> decryptor;
EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); }; void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); };
void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); };
};
public: void SetLeaseSetUpdated();
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool IsPublic() const { return m_IsPublic; };
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
void Start (); void SetPublic(bool pub) { m_IsPublic = pub; };
void Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; protected:
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
// ref counter // implements GarlicDestination
int Acquire () { return ++m_RefCounter; }; void HandleI2NPMessage(const uint8_t *buf, size_t len);
int Release () { return --m_RefCounter; };
int GetRefCounter () const { return m_RefCounter; };
// streaming bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID);
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination (int port);
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing (const i2p::data::IdentHash& to);
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
// datagram void SetLeaseSet(std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
// implements LocalDestination int GetLeaseSetType() const { return m_LeaseSetType; };
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;
protected: void SetLeaseSetType(int leaseSetType) { m_LeaseSetType = leaseSetType; };
void CleanupDestination (); int GetAuthType() const { return m_AuthType; };
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
private: virtual void CleanupDestination() {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage(const uint8_t *buf, size_t len) = 0;
std::shared_ptr<ClientDestination> GetSharedFromThis () { virtual void
return std::static_pointer_cast<ClientDestination>(shared_from_this ()); CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels) = 0;
}
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
private: private:
i2p::data::PrivateKeys m_Keys; void UpdateLeaseSet();
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
int m_StreamingAckDelay; std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt();
bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
int m_RefCounter; // how many clients(tunnels) use this destination
boost::asio::deadline_timer m_ReadyChecker; void Publish();
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys; // we don't need them for I2CP void HandlePublishConfirmationTimer(const boost::system::error_code &ecode);
public: void HandlePublishVerificationTimer(const boost::system::error_code &ecode);
// for HTTP only void HandlePublishDelayTimer(const boost::system::error_code &ecode);
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
bool DeleteStream (uint32_t recvStreamID);
};
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination void HandleDatabaseStoreMessage(const uint8_t *buf, size_t len);
{
public:
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr); void HandleDatabaseSearchReplyMessage(const uint8_t *buf, size_t len);
~RunnableClientDestination ();
void Start (); void HandleDeliveryStatusMessage(uint32_t msgID);
void Stop ();
};
} void RequestLeaseSet(const i2p::data::IdentHash &dest, RequestComplete requestComplete,
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest(const i2p::data::IdentHash &dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill,
std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer(const boost::system::error_code &ecode, const i2p::data::IdentHash &dest);
void HandleCleanupTimer(const boost::system::error_code &ecode);
void CleanupRemoteLeaseSets();
i2p::data::CryptoKeyType GetPreferredCryptoType() const;
private:
boost::asio::io_service &m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::mutex m_LeaseSetMutex;
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer;
std::string m_Nickname;
int m_LeaseSetType, m_AuthType;
std::unique_ptr<i2p::data::Tag<32> > m_LeaseSetPrivKey; // non-null if presented
public:
// for HTTP only
int GetNumRemoteLeaseSets() const { return m_RemoteLeaseSets.size(); };
const decltype(m_RemoteLeaseSets)
&
GetLeaseSets() const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet() const {
return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
};
bool IsPerClientAuth() const { return m_AuthType > 0; };
};
class ClientDestination : public LeaseSetDestination {
struct EncryptionKey {
uint8_t pub[256], priv[256];
i2p::data::CryptoKeyType keyType;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> 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,
bool isPublic, const std::map<std::string, std::string> *params = nullptr);
~ClientDestination();
void Start();
void Stop();
const i2p::data::PrivateKeys &GetPrivateKeys() const { return m_Keys; };
void Sign(const uint8_t *buf, int len, uint8_t *signature) const { m_Keys.Sign(buf, len, signature); };
// ref counter
int Acquire() { return ++m_RefCounter; };
int Release() { return --m_RefCounter; };
int GetRefCounter() const { return m_RefCounter; };
// streaming
std::shared_ptr<i2p::stream::StreamingDestination>
CreateStreamingDestination(int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination(int port = 0) const;
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination(int port);
// following methods operate with default streaming destination
void
CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash &dest, int port = 0);
void CreateStream(StreamRequestComplete streamRequestComplete,
std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream>
CreateStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing(const i2p::data::IdentHash &to);
void SendPing(std::shared_ptr<const i2p::data::BlindedPublicKey> to);
void AcceptStreams(const i2p::stream::StreamingDestination::Acceptor &acceptor);
void StopAcceptingStreams();
bool IsAcceptingStreams() const;
void AcceptOnce(const i2p::stream::StreamingDestination::Acceptor &acceptor);
int GetStreamingAckDelay() const { return m_StreamingAckDelay; }
bool IsStreamingAnswerPings() const { return m_IsStreamingAnswerPings; }
// datagram
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, i2p::data::CryptoKeyType preferredCrypto) const;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity() const { return m_Keys.GetPublic(); };
bool SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const;
const uint8_t *GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const;
protected:
void CleanupDestination();
// I2CP
void HandleDataMessage(const uint8_t *buf, size_t len);
void CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels);
private:
std::shared_ptr<ClientDestination> GetSharedFromThis() {
return std::static_pointer_cast<ClientDestination>(shared_from_this());
}
void PersistTemporaryKeys(EncryptionKey *keys, bool isSingleKey);
void ReadAuthKey(const std::string &group, const std::map<std::string, std::string> *params);
private:
i2p::data::PrivateKeys m_Keys;
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
int m_StreamingAckDelay;
bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination *m_DatagramDestination;
int m_RefCounter; // how many clients(tunnels) use this destination
boost::asio::deadline_timer m_ReadyChecker;
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys; // we don't need them for I2CP
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams() const;
bool DeleteStream(uint32_t recvStreamID);
};
class RunnableClientDestination : private i2p::util::RunnableService, public ClientDestination {
public:
RunnableClientDestination(const i2p::data::PrivateKeys &keys, bool isPublic,
const std::map<std::string, std::string> *params = nullptr);
~RunnableClientDestination();
void Start();
void Stop();
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -21,233 +21,276 @@
#include "Garlic.h" #include "Garlic.h"
#include "Tag.h" #include "Tag.h"
namespace i2p namespace i2p {
{ namespace garlic {
namespace garlic const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after
{ const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ 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 */ // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */
class RatchetTagSet class RatchetTagSet {
{ public:
public:
RatchetTagSet () {}; RatchetTagSet() {};
virtual ~RatchetTagSet () {};
void DHInitialize (const uint8_t * rootKey, const uint8_t * k); virtual ~RatchetTagSet() {};
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);
void DeleteSymmKey (int index);
int GetTagSetID () const { return m_TagSetID; }; void DHInitialize(const uint8_t *rootKey, const uint8_t *k);
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
private: void NextSessionTagRatchet();
i2p::data::Tag<64> m_SessionTagKeyData; uint64_t GetNextSessionTag();
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
int m_NextIndex, m_NextSymmKeyIndex;
std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
int m_TagSetID = 0; const uint8_t *GetNextRootKey() const { return m_NextRootKey; };
};
class ECIESX25519AEADRatchetSession; int GetNextIndex() const { return m_NextIndex; };
class ReceiveRatchetTagSet: public RatchetTagSet,
public std::enable_shared_from_this<ReceiveRatchetTagSet>
{
public:
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false): void GetSymmKey(int index, uint8_t *key);
m_Session (session), m_IsNS (isNS) {};
bool IsNS () const { return m_IsNS; }; void DeleteSymmKey(int index);
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
int GetTrimBehind () const { return m_TrimBehindIndex; };
void Expire (); int GetTagSetID() const { return m_TagSetID; };
bool IsExpired (uint64_t ts) const;
virtual bool IsIndexExpired (int index) const; void SetTagSetID(int tagsetID) { m_TagSetID = tagsetID; };
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index);
private: private:
int m_TrimBehindIndex = 0; i2p::data::Tag<64> m_SessionTagKeyData;
std::shared_ptr<ECIESX25519AEADRatchetSession> m_Session; uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
bool m_IsNS; int m_NextIndex, m_NextSymmKeyIndex;
uint64_t m_ExpirationTimestamp = 0; std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
};
class SymmetricKeyTagSet: public ReceiveRatchetTagSet int m_TagSetID = 0;
{ };
public:
SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); class ECIESX25519AEADRatchetSession;
bool IsIndexExpired (int index) const { return false; }; class ReceiveRatchetTagSet : public RatchetTagSet,
bool HandleNextMessage (uint8_t * buf, size_t len, int index); public std::enable_shared_from_this<ReceiveRatchetTagSet> {
public:
private: ReceiveRatchetTagSet(std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false) :
m_Session(session), m_IsNS(isNS) {};
GarlicDestination * m_Destination; bool IsNS() const { return m_IsNS; };
uint8_t m_Key[32];
};
enum ECIESx25519BlockType std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession() { return m_Session; };
{
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextKey = 7,
eECIESx25519BlkAck = 8,
eECIESx25519BlkAckRequest = 9,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01; void SetTrimBehind(int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, int GetTrimBehind() const { return m_TrimBehindIndex; };
private i2p::crypto::NoiseSymmetricState,
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{
enum SessionState
{
eSessionStateNew = 0,
eSessionStateNewSessionReceived,
eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent,
eSessionStateEstablished,
eSessionStateOneTime
};
struct DHRatchet void Expire();
{
int keyID = 0;
std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key
bool newKey = true;
};
public: bool IsExpired(uint64_t ts) const;
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS); virtual bool IsIndexExpired(int index) const;
~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0); virtual bool HandleNextMessage(uint8_t *buf, size_t len, int index);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } private:
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
void Terminate () { m_IsTerminated = true; } int m_TrimBehindIndex = 0;
void SetDestination (const i2p::data::IdentHash& dest) // TODO: std::shared_ptr<ECIESX25519AEADRatchetSession> m_Session;
{ bool m_IsNS;
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); uint64_t m_ExpirationTimestamp = 0;
} };
bool CheckExpired (uint64_t ts); // true is expired class SymmetricKeyTagSet : public ReceiveRatchetTagSet {
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } public:
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool IsRatchets () const { return true; }; SymmetricKeyTagSet(GarlicDestination *destination, const uint8_t *key);
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
bool IsTerminated () const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
protected: bool IsIndexExpired(int index) const { return false; };
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; }; bool HandleNextMessage(uint8_t *buf, size_t len, int index);
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
private: private:
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes GarlicDestination *m_Destination;
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const; uint8_t m_Key[32];
};
bool HandleNewIncomingSession (const uint8_t * buf, size_t len); enum ECIESx25519BlockType {
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len); eECIESx25519BlkDateTime = 0,
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index); eECIESx25519BlkSessionID = 1,
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset); eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextKey = 7,
eECIESx25519BlkAck = 8,
eECIESx25519BlkAckRequest = 9,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true); const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01;
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
size_t CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload); class ECIESX25519AEADRatchetSession : public GarlicRoutingSession,
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len); private i2p::crypto::NoiseSymmetricState,
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len); public std::enable_shared_from_this<ECIESX25519AEADRatchetSession> {
enum SessionState {
eSessionStateNew = 0,
eSessionStateNewSessionReceived,
eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent,
eSessionStateEstablished,
eSessionStateOneTime
};
void GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags); struct DHRatchet {
void NewNextSendRatchet (); int keyID = 0;
std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key
bool newKey = true;
};
private: public:
uint8_t m_RemoteStaticKey[32]; ECIESX25519AEADRatchetSession(GarlicDestination *owner, bool attachLeaseSetNS);
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
SessionState m_State = eSessionStateNew;
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
m_LastSentTimestamp = 0; // in milliseconds
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
public: ~ECIESX25519AEADRatchetSession();
// for HTTP only bool HandleNextMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
int GetState () const { return (int)m_State; } int index = 0);
i2p::data::IdentHash GetDestination () const
{
return m_Destination ? *m_Destination : i2p::data::IdentHash ();
}
};
// single session for all incoming messages std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg);
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession
{
public:
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); std::shared_ptr<I2NPMessage> WrapOneTimeMessage(std::shared_ptr<const I2NPMessage> msg);
bool HandleNextMessage (const uint8_t * buf, size_t len);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
private: const uint8_t *GetRemoteStaticKey() const { return m_RemoteStaticKey; }
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; void SetRemoteStaticKey(const uint8_t *key) { memcpy(m_RemoteStaticKey, key, 32); }
};
std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag); void Terminate() { m_IsTerminated = true; }
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey);
} 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_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT;
}
bool IsInactive(uint64_t ts) const {
return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted(ts);
}
bool IsRatchets() const { return true; };
bool IsReadyToSend() const { return m_State != eSessionStateNewSessionSent; };
bool IsTerminated() const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp() const { return m_LastActivityTimestamp; };
protected:
i2p::crypto::NoiseSymmetricState &GetNoiseState() { return *this; };
void SetNoiseState(const i2p::crypto::NoiseSymmetricState &state) { GetNoiseState() = state; };
void CreateNonce(uint64_t seqn, uint8_t *nonce);
void
HandlePayload(const uint8_t *buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet> &receiveTagset,
int index);
private:
bool GenerateEphemeralKeysAndEncode(uint8_t *buf); // buf is 32 bytes
void InitNewSessionTagset(std::shared_ptr<RatchetTagSet> tagsetNsr) const;
bool HandleNewIncomingSession(const uint8_t *buf, size_t len);
bool HandleNewOutgoingSessionReply(uint8_t *buf, size_t len);
bool
HandleExistingSessionMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
int index);
void
HandleNextKey(const uint8_t *buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet> &receiveTagset);
bool NewOutgoingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen,
bool isStatic = true);
bool NewSessionReplyMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen);
bool NextNewSessionReplyMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen);
bool NewExistingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen);
size_t CreatePayload(std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t *payload);
size_t CreateGarlicClove(std::shared_ptr<const I2NPMessage> msg, uint8_t *buf, size_t len);
size_t CreateLeaseSetClove(std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t *buf,
size_t len);
void GenerateMoreReceiveTags(std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags);
void NewNextSendRatchet();
private:
uint8_t m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
SessionState m_State = eSessionStateNew;
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
m_LastSentTimestamp = 0; // in milliseconds
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
public:
// for HTTP only
int GetState() const { return (int) m_State; }
i2p::data::IdentHash GetDestination() const {
return m_Destination ? *m_Destination : i2p::data::IdentHash();
}
};
// single session for all incoming messages
class RouterIncomingRatchetSession : public ECIESX25519AEADRatchetSession {
public:
RouterIncomingRatchetSession(const i2p::crypto::NoiseSymmetricState &initState);
bool HandleNextMessage(const uint8_t *buf, size_t len);
i2p::crypto::NoiseSymmetricState &GetCurrentNoiseState() { return m_CurrentNoiseState; };
private:
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
};
std::shared_ptr<I2NPMessage>
WrapECIESX25519Message(std::shared_ptr<const I2NPMessage> msg, const uint8_t *key, uint64_t tag);
std::shared_ptr<I2NPMessage>
WrapECIESX25519MessageForRouter(std::shared_ptr<const I2NPMessage> msg, const uint8_t *routerPublicKey);
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -13,127 +13,168 @@
#include <openssl/bn.h> #include <openssl/bn.h>
#include "Crypto.h" #include "Crypto.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto struct EDDSAPoint {
{ BIGNUM *x{nullptr};
struct EDDSAPoint BIGNUM *y{nullptr};
{ BIGNUM *z{nullptr};
BIGNUM * x {nullptr}; BIGNUM *t{nullptr}; // projective coordinates
BIGNUM * y {nullptr};
BIGNUM * z {nullptr};
BIGNUM * t {nullptr}; // projective coordinates
EDDSAPoint () {} EDDSAPoint() {}
EDDSAPoint (const EDDSAPoint& other) { *this = other; }
EDDSAPoint (EDDSAPoint&& other) { *this = std::move (other); }
EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr)
: x(x1)
, y(y1)
, z(z1)
, t(t1)
{}
~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); }
EDDSAPoint& operator=(EDDSAPoint&& other) EDDSAPoint(const EDDSAPoint &other) { *this = other; }
{
if (this != &other)
{
BN_free (x); x = other.x; other.x = nullptr;
BN_free (y); y = other.y; other.y = nullptr;
BN_free (z); z = other.z; other.z = nullptr;
BN_free (t); t = other.t; other.t = nullptr;
}
return *this;
}
EDDSAPoint& operator=(const EDDSAPoint& other) EDDSAPoint(EDDSAPoint &&other) { *this = std::move(other); }
{
if (this != &other)
{
BN_free (x); x = other.x ? BN_dup (other.x) : nullptr;
BN_free (y); y = other.y ? BN_dup (other.y) : nullptr;
BN_free (z); z = other.z ? BN_dup (other.z) : nullptr;
BN_free (t); t = other.t ? BN_dup (other.t) : nullptr;
}
return *this;
}
EDDSAPoint operator-() const EDDSAPoint(BIGNUM *x1, BIGNUM *y1, BIGNUM *z1 = nullptr, BIGNUM *t1 = nullptr)
{ : x(x1), y(y1), z(z1), t(t1) {}
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL;
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); };
if (y) y1 = BN_dup (y);
if (z) z1 = BN_dup (z);
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); };
return EDDSAPoint {x1, y1, z1, t1};
}
};
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; ~EDDSAPoint() {
const size_t EDDSA25519_SIGNATURE_LENGTH = 64; BN_free(x);
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; BN_free(y);
class Ed25519 BN_free(z);
{ BN_free(t);
public: }
Ed25519 (); EDDSAPoint &operator=(EDDSAPoint &&other) {
Ed25519 (const Ed25519& other); if (this != &other) {
~Ed25519 (); BN_free(x);
x = other.x;
other.x = nullptr;
BN_free(y);
y = other.y;
other.y = nullptr;
BN_free(z);
z = other.z;
other.z = nullptr;
BN_free(t);
t = other.t;
other.t = nullptr;
}
return *this;
}
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const; EDDSAPoint &operator=(const EDDSAPoint &other) {
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const; if (this != &other) {
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const; BN_free(x);
#if !OPENSSL_X25519 x = other.x ? BN_dup(other.x) : nullptr;
void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519 BN_free(y);
void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; y = other.y ? BN_dup(other.y) : nullptr;
#endif BN_free(z);
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 z = other.z ? BN_dup(other.z) : nullptr;
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 BN_free(t);
t = other.t ? BN_dup(other.t) : nullptr;
}
return *this;
}
bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const; EDDSAPoint operator-() const {
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; BIGNUM *x1 = NULL, *y1 = NULL, *z1 = NULL, *t1 = NULL;
void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; if (x) {
x1 = BN_dup(x);
BN_set_negative(x1, !BN_is_negative(x));
};
if (y) y1 = BN_dup(y);
if (z) z1 = BN_dup(z);
if (t) {
t1 = BN_dup(t);
BN_set_negative(t1, !BN_is_negative(t));
};
return EDDSAPoint{x1, y1, z1, t1};
}
};
static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
void CreateRedDSAPrivateKey (uint8_t * priv); // priv is 32 bytes const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
private: class Ed25519 {
public:
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const; Ed25519();
void Double (EDDSAPoint& p, BN_CTX * ctx) const;
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; Ed25519(const Ed25519 &other);
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;
template<int len> ~Ed25519();
BIGNUM * DecodeBN (const uint8_t * buf) const;
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const; EDDSAPoint GeneratePublicKey(const uint8_t *expandedPrivateKey, BN_CTX *ctx) const;
EDDSAPoint DecodePublicKey(const uint8_t *buf, BN_CTX *ctx) const;
void EncodePublicKey(const EDDSAPoint &publicKey, uint8_t *buf, BN_CTX *ctx) const;
#if !OPENSSL_X25519 #if !OPENSSL_X25519
// for x25519
BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const; void ScalarMul(const uint8_t *p, const uint8_t *e, uint8_t *buf,
BN_CTX *ctx) const; // p is point, e is number for x25519
void ScalarMulB(const uint8_t *e, uint8_t *buf, BN_CTX *ctx) const;
#endif #endif
private: void BlindPublicKey(const uint8_t *pub, const uint8_t *seed,
uint8_t *blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPrivateKey(const uint8_t *priv, const uint8_t *seed, uint8_t *blindedPriv,
uint8_t *blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
BIGNUM * q, * l, * d, * I; bool Verify(const EDDSAPoint &publicKey, const uint8_t *digest, const uint8_t *signature) const;
// transient values
BIGNUM * two_252_2; // 2^252-2
EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes
// 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<Ed25519>& GetEd25519 (); 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;
void Double(EDDSAPoint &p, BN_CTX *ctx) const;
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;
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;
template<int len>
BIGNUM *DecodeBN(const uint8_t *buf) const;
void EncodeBN(const BIGNUM *bn, uint8_t *buf, size_t len) const;
#if !OPENSSL_X25519
// for x25519
BIGNUM *ScalarMul(const BIGNUM *p, const BIGNUM *e, BN_CTX *ctx) const;
#endif
private:
BIGNUM *q, *l, *d, *I;
// transient values
BIGNUM *two_252_2; // 2^252-2
EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes
// 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<Ed25519> &GetEd25519();
}
} }
#endif #endif

View file

@ -10,205 +10,209 @@
#include "Crypto.h" #include "Crypto.h"
#include "Elligator.h" #include "Elligator.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
Elligator2::Elligator2 () Elligator2::Elligator2() {
{ // TODO: share with Ed22519
// TODO: share with Ed22519 p = BN_new();
p = BN_new (); // 2^255-19
// 2^255-19 BN_set_bit(p, 255); // 2^255
BN_set_bit (p, 255); // 2^255 BN_sub_word(p, 19);
BN_sub_word (p, 19); p38 = BN_dup(p);
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8 BN_add_word(p38, 3);
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2 BN_div_word(p38, 8); // (p+3)/8
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4 p12 = BN_dup(p);
BN_sub_word(p12, 1);
BN_div_word(p12, 2); // (p-1)/2
p14 = BN_dup(p);
BN_sub_word(p14, 1);
BN_div_word(p14, 4); // (p-1)/4
A = BN_new (); BN_set_word (A, 486662); A = BN_new();
nA = BN_new (); BN_sub (nA, p, A); BN_set_word(A, 486662);
nA = BN_new();
BN_sub(nA, p, A);
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
// calculate sqrt(-1) // calculate sqrt(-1)
sqrtn1 = BN_new (); 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 BN_mod_exp(sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
u = BN_new (); BN_set_word (u, 2); u = BN_new();
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx); BN_set_word(u, 2);
iu = BN_new();
BN_mod_inverse(iu, u, p, ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
} }
Elligator2::~Elligator2 () Elligator2::~Elligator2() {
{ BN_free(p);
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14); BN_free(p38);
BN_free (sqrtn1); BN_free (A); BN_free (nA); BN_free(p12);
BN_free (u); BN_free (iu); BN_free(p14);
} 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 Elligator2::Encode(const uint8_t *key, uint8_t *encoded, bool highY, bool random) const {
{ bool ret = true;
bool ret = true; BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); BN_CTX_start(ctx);
BN_CTX_start (ctx);
uint8_t key1[32]; uint8_t key1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian for (size_t i = 0; i < 16; i++) // from Little Endian
{ {
key1[i] = key[31 - i]; key1[i] = key[31 - i];
key1[31 - i] = key[i]; key1[31 - i] = key[i];
} }
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x); BIGNUM *x = BN_CTX_get(ctx);
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A BN_bin2bn(key1, 32, x);
BN_sub (xA, p, xA); // p - (x + A) BIGNUM *xA = BN_CTX_get(ctx);
BN_add(xA, x, A); // x + A
BN_sub(xA, p, xA); // p - (x + A)
BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA BIGNUM *uxxA = BN_CTX_get(ctx); // u*x*xA
BN_mod_mul (uxxA, u, x, p, ctx); 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) 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) {
if (random) RAND_bytes(&randByte, 1);
{ highY = randByte & 0x01;
RAND_bytes (&randByte, 1); }
highY = randByte & 0x01;
}
BIGNUM * r = BN_CTX_get (ctx); BIGNUM *r = BN_CTX_get(ctx);
if (highY) if (highY) {
{ BN_mod_inverse(r, x, p, ctx);
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);
else BN_mod_mul(r, r, x, p, ctx);
{ }
BN_mod_inverse (r, xA, p, ctx); BN_mod_mul(r, r, iu, p, ctx);
BN_mod_mul (r, r, x, p, ctx);
}
BN_mod_mul (r, r, iu, p, ctx);
SquareRoot (r, r, ctx); SquareRoot(r, r, ctx);
bn2buf (r, encoded, 32); bn2buf(r, encoded, 32);
if (random) 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 for (size_t i = 0; i < 16; i++) // To Little Endian
{ {
uint8_t tmp = encoded[i]; uint8_t tmp = encoded[i];
encoded[i] = encoded[31 - i]; encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp; encoded[31 - i] = tmp;
} }
} } else
else ret = false;
ret = false;
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return ret; return ret;
} }
bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const bool Elligator2::Decode(const uint8_t *encoded, uint8_t *key) const {
{ bool ret = true;
bool ret = true; BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); BN_CTX_start(ctx);
BN_CTX_start (ctx);
uint8_t encoded1[32]; uint8_t encoded1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian for (size_t i = 0; i < 16; i++) // from Little Endian
{ {
encoded1[i] = encoded[31 - i]; encoded1[i] = encoded[31 - i];
encoded1[31 - i] = encoded[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); BIGNUM *r = BN_CTX_get(ctx);
BN_bin2bn(encoded1, 32, r);
if (BN_cmp (r, p12) <= 0) // r < (p-1)/2 if (BN_cmp(r, p12) <= 0) // r < (p-1)/2
{ {
// v = -A/(1+u*r^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_mul (v, v, u, p, ctx); BN_mod_sqr(v, r, p, ctx);
BN_add_word (v, 1); BN_mod_mul(v, v, u, p, ctx);
BN_mod_inverse (v, v, p, ctx); BN_add_word(v, 1);
BN_mod_mul (v, v, nA, p, ctx); BN_mod_inverse(v, v, p, ctx);
BN_mod_mul(v, v, nA, p, ctx);
BIGNUM * vpA = BN_CTX_get (ctx); BIGNUM *vpA = BN_CTX_get(ctx);
BN_add (vpA, v, A); // v + A BN_add(vpA, v, A); // v + A
// t = v^3+A*v^2+v = v^2*(v+A)+v // 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); BIGNUM *t = BN_CTX_get(ctx);
BN_mod_mul (t, t, vpA, p, ctx); BN_mod_sqr(t, v, p, ctx);
BN_mod_add (t, 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); int legendre = Legendre(t, ctx);
BIGNUM * x = BN_CTX_get (ctx); BIGNUM *x = BN_CTX_get(ctx);
if (legendre == 1) if (legendre == 1)
BN_copy (x, v); BN_copy(x, v);
else else {
{ BN_sub(x, p, v);
BN_sub (x, p, v); BN_mod_sub(x, x, A, p, ctx);
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 for (size_t i = 0; i < 16; i++) // To Little Endian
{ {
uint8_t tmp = key[i]; uint8_t tmp = key[i];
key[i] = key[31 - i]; key[i] = key[31 - i];
key[31 - i] = tmp; key[31 - i] = tmp;
} }
} } else
else ret = false;
ret = false;
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return ret; return ret;
} }
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const void Elligator2::SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const {
{ BIGNUM *t = BN_CTX_get(ctx);
BIGNUM * t = BN_CTX_get (ctx); BN_mod_exp(t, x, p14, p, ctx); // t = x^((p-1)/4)
BN_mod_exp (t, x, p14, p, ctx); // t = x^((p-1)/4) BN_mod_exp(r, x, p38, p, ctx); // r = x^((p+3)/8)
BN_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8) BN_add_word(t, 1);
BN_add_word (t, 1);
if (!BN_cmp (t, p)) 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 if (BN_cmp(r, p12) > 0) // r > (p-1)/2
BN_sub (r, p, r); BN_sub(r, p, r);
} }
int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const 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
// 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);
BIGNUM * r = BN_CTX_get (ctx); BN_mod_exp(r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
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;
return 1; else if (BN_is_zero(r))
else if (BN_is_zero(r)) return 0;
return 0; return -1;
return -1; }
}
static std::unique_ptr<Elligator2> g_Elligator; static std::unique_ptr<Elligator2> g_Elligator;
std::unique_ptr<Elligator2>& GetElligator ()
{ std::unique_ptr<Elligator2> &GetElligator() {
if (!g_Elligator) if (!g_Elligator) {
{ auto el = new Elligator2();
auto el = new Elligator2(); if (!g_Elligator) // make sure it was not created already
if (!g_Elligator) // make sure it was not created already g_Elligator.reset(el);
g_Elligator.reset (el); else
else delete el;
delete el; }
} return g_Elligator;
return g_Elligator; }
} }
}
} }

View file

@ -13,33 +13,33 @@
#include <memory> #include <memory>
#include <openssl/bn.h> #include <openssl/bn.h>
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
class Elligator2 class Elligator2 {
{ public:
public:
Elligator2 (); Elligator2();
~Elligator2 ();
bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const; ~Elligator2();
bool Decode (const uint8_t * encoded, uint8_t * key) const;
private: bool Encode(const uint8_t *key, uint8_t *encoded, bool highY = false, bool random = true) const;
void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const; bool Decode(const uint8_t *encoded, uint8_t *key) const;
int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p
private: private:
BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu; void SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const;
};
std::unique_ptr<Elligator2>& GetElligator (); int Legendre(const BIGNUM *a, BN_CTX *ctx) const; // a/p
}
private:
BIGNUM *p, *p38, *p12, *p14, *sqrtn1, *A, *nA, *u, *iu;
};
std::unique_ptr <Elligator2> &GetElligator();
}
} }
#endif #endif

View file

@ -21,273 +21,267 @@
#include "Garlic.h" #include "Garlic.h"
namespace i2p { namespace i2p {
namespace fs { namespace fs {
std::string appName = "i2pd"; std::string appName = "i2pd";
std::string dataDir = ""; std::string dataDir = "";
std::string certsDir = ""; std::string certsDir = "";
#ifdef _WIN32 #ifdef _WIN32
std::string dirSep = "\\"; std::string dirSep = "\\";
#else #else
std::string dirSep = "/"; std::string dirSep = "/";
#endif #endif
const std::string & GetAppName () { const std::string &GetAppName() {
return appName; return appName;
} }
void SetAppName (const std::string& name) { void SetAppName(const std::string &name) {
appName = name; appName = name;
} }
const std::string & GetDataDir () { const std::string &GetDataDir() {
return dataDir; return dataDir;
} }
const std::string & GetCertsDir () { const std::string &GetCertsDir() {
return certsDir; return certsDir;
} }
const std::string GetUTF8DataDir () { const std::string GetUTF8DataDir() {
#ifdef _WIN32 #ifdef _WIN32
boost::filesystem::wpath path (dataDir); boost::filesystem::wpath path (dataDir);
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8 auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
auto dataDirUTF8 = path.string(); auto dataDirUTF8 = path.string();
boost::filesystem::path::imbue(loc); // Return locale settings back boost::filesystem::path::imbue(loc); // Return locale settings back
return dataDirUTF8; return dataDirUTF8;
#else #else
return dataDir; // linux, osx, android uses UTF-8 by default return dataDir; // linux, osx, android uses UTF-8 by default
#endif #endif
} }
void DetectDataDir(const std::string & cmdline_param, bool isService) { void DetectDataDir(const std::string &cmdline_param, bool isService) {
// with 'datadir' option // with 'datadir' option
if (cmdline_param != "") { if (cmdline_param != "") {
dataDir = cmdline_param; dataDir = cmdline_param;
return; return;
} }
#if !defined(MAC_OSX) && !defined(ANDROID) #if !defined(MAC_OSX) && !defined(ANDROID)
// with 'service' option // with 'service' option
if (isService) { if (isService) {
#ifdef _WIN32 #ifdef _WIN32
wchar_t commonAppData[MAX_PATH]; wchar_t commonAppData[MAX_PATH];
if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK) if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK)
{ {
#ifdef WIN32_APP #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get common AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get common AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else #else
fprintf(stderr, "Error: Unable to get common AppData path!"); fprintf(stderr, "Error: Unable to get common AppData path!");
#endif #endif
exit(1); exit(1);
} }
else else
{ {
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
} }
#else #else
dataDir = "/var/lib/" + appName; dataDir = "/var/lib/" + appName;
#endif #endif
return; return;
} }
#endif #endif
// detect directory as usual // detect directory as usual
#ifdef _WIN32 #ifdef _WIN32
wchar_t localAppData[MAX_PATH]; wchar_t localAppData[MAX_PATH];
// check executable directory first // check executable directory first
if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH)) if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH))
{ {
#ifdef WIN32_APP #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else #else
fprintf(stderr, "Error: Unable to get application path!"); fprintf(stderr, "Error: Unable to get application path!");
#endif #endif
exit(1); exit(1);
} }
else else
{ {
auto execPath = boost::filesystem::wpath(localAppData).parent_path(); auto execPath = boost::filesystem::wpath(localAppData).parent_path();
// if config file exists in .exe's folder use it // if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
{ {
dataDir = execPath.string (); dataDir = execPath.string ();
} else // otherwise %appdata% } else // otherwise %appdata%
{ {
if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
{ {
#ifdef WIN32_APP #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else #else
fprintf(stderr, "Error: Unable to get AppData path!"); fprintf(stderr, "Error: Unable to get AppData path!");
#endif #endif
exit(1); exit(1);
} }
else else
{ {
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
} }
} }
} }
return; return;
#elif defined(MAC_OSX) #elif defined(MAC_OSX)
char *home = getenv("HOME"); char *home = getenv("HOME");
dataDir = (home != NULL && strlen(home) > 0) ? home : ""; dataDir = (home != NULL && strlen(home) > 0) ? home : "";
dataDir += "/Library/Application Support/" + appName; dataDir += "/Library/Application Support/" + appName;
return; return;
#else /* other unix */ #else /* other unix */
#if defined(ANDROID) #if defined(ANDROID)
const char * ext = getenv("EXTERNAL_STORAGE"); const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard"; if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext)) if (boost::filesystem::exists(ext))
{ {
dataDir = std::string (ext) + "/" + appName; dataDir = std::string (ext) + "/" + appName;
return; return;
} }
#endif // ANDROID #endif // ANDROID
// use /home/user/.i2pd or /tmp/i2pd // use /home/user/.i2pd or /tmp/i2pd
char *home = getenv("HOME"); char *home = getenv("HOME");
if (home != NULL && strlen(home) > 0) { if (home != NULL && strlen(home) > 0) {
dataDir = std::string(home) + "/." + appName; dataDir = std::string(home) + "/." + appName;
} else { } else {
dataDir = "/tmp/" + appName; dataDir = "/tmp/" + appName;
} }
return; return;
#endif #endif
} }
void SetCertsDir(const std::string & cmdline_certsdir) { void SetCertsDir(const std::string &cmdline_certsdir) {
if (cmdline_certsdir != "") if (cmdline_certsdir != "") {
{ if (cmdline_certsdir[cmdline_certsdir.length() - 1] == '/')
if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/') certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size() - 1); // strip trailing slash
certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash else
else certsDir = cmdline_certsdir;
certsDir = cmdline_certsdir; } else {
} certsDir = i2p::fs::DataDirPath("certificates");
else }
{ return;
certsDir = i2p::fs::DataDirPath("certificates"); }
}
return;
}
bool Init() { bool Init() {
if (!boost::filesystem::exists(dataDir)) if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir); boost::filesystem::create_directory(dataDir);
std::string destinations = DataDirPath("destinations"); std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations)) if (!boost::filesystem::exists(destinations))
boost::filesystem::create_directory(destinations); boost::filesystem::create_directory(destinations);
std::string tags = DataDirPath("tags"); std::string tags = DataDirPath("tags");
if (!boost::filesystem::exists(tags)) if (!boost::filesystem::exists(tags))
boost::filesystem::create_directory(tags); boost::filesystem::create_directory(tags);
else else
i2p::garlic::CleanUpTagsFiles (); i2p::garlic::CleanUpTagsFiles();
return true; return true;
} }
bool ReadDir(const std::string & path, std::vector<std::string> & files) { bool ReadDir(const std::string &path, std::vector<std::string> &files) {
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
return false; return false;
boost::filesystem::directory_iterator it(path); boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end; boost::filesystem::directory_iterator end;
for ( ; it != end; it++) { for (; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status())) if (!boost::filesystem::is_regular_file(it->status()))
continue; continue;
files.push_back(it->path().string()); files.push_back(it->path().string());
} }
return true; return true;
} }
bool Exists(const std::string & path) { bool Exists(const std::string &path) {
return boost::filesystem::exists(path); return boost::filesystem::exists(path);
} }
uint32_t GetLastUpdateTime (const std::string & path) uint32_t GetLastUpdateTime(const std::string &path) {
{ if (!boost::filesystem::exists(path))
if (!boost::filesystem::exists(path)) return 0;
return 0; boost::system::error_code ec;
boost::system::error_code ec; auto t = boost::filesystem::last_write_time(path, ec);
auto t = boost::filesystem::last_write_time (path, ec); return ec ? 0 : t;
return ec ? 0 : t; }
}
bool Remove(const std::string & path) { bool Remove(const std::string &path) {
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
return false; return false;
return boost::filesystem::remove(path); return boost::filesystem::remove(path);
} }
bool CreateDirectory (const std::string& path) bool CreateDirectory(const std::string &path) {
{ if (boost::filesystem::exists(path) && boost::filesystem::is_directory(boost::filesystem::status(path)))
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
return true; return boost::filesystem::create_directory(path);
return boost::filesystem::create_directory(path); }
}
void HashedStorage::SetPlace(const std::string &path) { void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name; root = path + i2p::fs::dirSep + name;
} }
bool HashedStorage::Init(const char * chars, size_t count) { bool HashedStorage::Init(const char *chars, size_t count) {
if (!boost::filesystem::exists(root)) { if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directories(root); boost::filesystem::create_directories(root);
} }
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p)) if (boost::filesystem::exists(p))
continue; continue;
if (boost::filesystem::create_directory(p)) if (boost::filesystem::create_directory(p))
continue; /* ^ throws exception on failure */ continue; /* ^ throws exception on failure */
return false; return false;
} }
return true; return true;
} }
std::string HashedStorage::Path(const std::string & ident) const { std::string HashedStorage::Path(const std::string &ident) const {
std::string safe_ident = ident; std::string safe_ident = ident;
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-'); std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-'); std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
std::stringstream t(""); std::stringstream t("");
t << this->root << i2p::fs::dirSep; t << this->root << i2p::fs::dirSep;
t << prefix1 << safe_ident[0] << i2p::fs::dirSep; t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
t << prefix2 << safe_ident << "." << suffix; t << prefix2 << safe_ident << "." << suffix;
return t.str(); return t.str();
} }
void HashedStorage::Remove(const std::string & ident) { void HashedStorage::Remove(const std::string &ident) {
std::string path = Path(ident); std::string path = Path(ident);
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
return; return;
boost::filesystem::remove(path); boost::filesystem::remove(path);
} }
void HashedStorage::Traverse(std::vector<std::string> & files) { void HashedStorage::Traverse(std::vector<std::string> &files) {
Iterate([&files] (const std::string & fname) { Iterate([&files](const std::string &fname) {
files.push_back(fname); files.push_back(fname);
}); });
} }
void HashedStorage::Iterate(FilenameVisitor v) void HashedStorage::Iterate(FilenameVisitor v) {
{ boost::filesystem::path p(root);
boost::filesystem::path p(root); boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator it(p); boost::filesystem::recursive_directory_iterator end;
boost::filesystem::recursive_directory_iterator end;
for ( ; it != end; it++) { for (; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() )) if (!boost::filesystem::is_regular_file(it->status()))
continue; continue;
const std::string & t = it->path().string(); const std::string &t = it->path().string();
v(t); v(t);
} }
} }
} // fs } // fs
} // i2p } // i2p

View file

@ -16,168 +16,175 @@
#include <functional> #include <functional>
namespace i2p { namespace i2p {
namespace fs { namespace fs {
extern std::string dirSep; extern std::string dirSep;
/** /**
* @brief Class to work with NetDb & Router profiles * @brief Class to work with NetDb & Router profiles
* *
* Usage: * Usage:
* *
* const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; * const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
* auto h = HashedStorage("name", "y", "z-", ".txt"); * auto h = HashedStorage("name", "y", "z-", ".txt");
* h.SetPlace("/tmp/hs-test"); * h.SetPlace("/tmp/hs-test");
* h.GetName() -> gives "name" * h.GetName() -> gives "name"
* h.GetRoot() -> gives "/tmp/hs-test/name" * h.GetRoot() -> gives "/tmp/hs-test/name"
* h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet * 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.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 * h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists
* std::vector<std::string> files; * std::vector<std::string> files;
* h.Traverse(files); <- finds all files in storage and saves in given vector * h.Traverse(files); <- finds all files in storage and saves in given vector
*/ */
class HashedStorage class HashedStorage {
{ protected:
protected:
std::string root; /**< path to storage with it's name included */ std::string root; /**< path to storage with it's name included */
std::string name; /**< name of the storage */ std::string name; /**< name of the storage */
std::string prefix1; /**< hashed directory prefix */ std::string prefix1; /**< hashed directory prefix */
std::string prefix2; /**< prefix of file in storage */ std::string prefix2; /**< prefix of file in storage */
std::string suffix; /**< suffix of file in storage (extension) */ std::string suffix; /**< suffix of file in storage (extension) */
public: public:
typedef std::function<void(const std::string &)> FilenameVisitor; typedef std::function<void(const std::string &)> 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 */ HashedStorage(const char *n, const char *p1, const char *p2, const char *s) :
bool Init(const char* chars, size_t cnt); name(n), prefix1(p1), prefix2(p2), suffix(s) {};
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<std::string> & files);
/** visit every file in this storage with a visitor */
void Iterate(FilenameVisitor v);
};
/** @brief Returns current application name, default 'i2pd' */ /** create subdirs in storage */
const std::string & GetAppName (); bool Init(const char *chars, size_t cnt);
/** @brief Set application name, affects autodetection of datadir */
void SetAppName (const std::string& name);
/** @brief Returns datadir path */ const std::string &GetRoot() const { return root; }
const std::string & GetDataDir();
/** @brief Returns certsdir path */ const std::string &GetName() const { return name; }
const std::string & GetCertsDir();
/** @brief Returns datadir path in UTF-8 encoding */ /** set directory where to place storage directory */
const std::string GetUTF8DataDir(); void SetPlace(const std::string &path);
/** /** path to file with given ident */
* @brief Set datadir either from cmdline option or using autodetection std::string Path(const std::string &ident) const;
* @param cmdline_param Value of cmdline parameter --datadir=<something>
* @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);
/** /** remove file by ident */
* @brief Set certsdir either from cmdline option or using autodetection void Remove(const std::string &ident);
* @param cmdline_param Value of cmdline parameter --certsdir=<something>
*
* Examples of autodetected paths:
*
* Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\certificates
* Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\certificates
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
* Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates
*/
void SetCertsDir(const std::string & cmdline_certsdir);
/** /** find all files in storage and store list in provided vector */
* @brief Create subdirectories inside datadir void Traverse(std::vector <std::string> &files);
*/
bool Init();
/** /** visit every file in this storage with a visitor */
* @brief Get list of files in directory void Iterate(FilenameVisitor v);
* @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<std::string> & files);
/** /** @brief Returns current application name, default 'i2pd' */
* @brief Remove file with given path const std::string &GetAppName();
* @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 Set application name, affects autodetection of datadir */
* @brief Check existence of file void SetAppName(const std::string &name);
* @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 /** @brief Returns datadir path */
const std::string &GetDataDir();
bool CreateDirectory (const std::string& path); /** @brief Returns certsdir path */
const std::string &GetCertsDir();
template<typename T> /** @brief Returns datadir path in UTF-8 encoding */
void _ExpandPath(std::stringstream & path, T c) { const std::string GetUTF8DataDir();
path << i2p::fs::dirSep << c;
}
template<typename T, typename ... Other> /**
void _ExpandPath(std::stringstream & path, T c, Other ... other) { * @brief Set datadir either from cmdline option or using autodetection
_ExpandPath(path, c); * @param cmdline_param Value of cmdline parameter --datadir=<something>
_ExpandPath(path, other ...); * @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 Get path relative to datadir * @brief Set certsdir either from cmdline option or using autodetection
* * @param cmdline_param Value of cmdline parameter --certsdir=<something>
* Examples (with datadir = "/tmp/i2pd"): *
* * Examples of autodetected paths:
* i2p::fs::Path("test") -> '/tmp/i2pd/test' *
* i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt' * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\certificates
*/ * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\certificates
template<typename ... Other> * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
std::string DataDirPath(Other ... components) { * Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates
std::stringstream s(""); */
s << i2p::fs::GetDataDir(); void SetCertsDir(const std::string &cmdline_certsdir);
_ExpandPath(s, components ...);
return s.str(); /**
} * @brief Create subdirectories inside datadir
*/
bool Init();
template<typename Storage, typename... Filename> /**
std::string StorageRootPath (const Storage& storage, Filename... filenames) * @brief Get list of files in directory
{ * @param path Path to directory
std::stringstream s(""); * @param files Vector to store found files
s << storage.GetRoot (); * @return true on success and false if directory not exists
_ExpandPath(s, filenames...); */
bool ReadDir(const std::string &path, std::vector <std::string> &files);
return s.str(); /**
} * @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);
} // fs /**
* @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
bool CreateDirectory(const std::string &path);
template<typename T>
void _ExpandPath(std::stringstream &path, T c) {
path << i2p::fs::dirSep << c;
}
template<typename T, typename ... Other>
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<typename ... Other>
std::string DataDirPath(Other ... components) {
std::stringstream s("");
s << i2p::fs::GetDataDir();
_ExpandPath(s, components ...);
return s.str();
}
template<typename Storage, typename... Filename>
std::string StorageRootPath(const Storage &storage, Filename... filenames) {
std::stringstream s("");
s << storage.GetRoot();
_ExpandPath(s, filenames...);
return s.str();
}
} // fs
} // i2p } // i2p
#endif // /* FS_H__ */ #endif // /* FS_H__ */

View file

@ -15,185 +15,160 @@
#include "Family.h" #include "Family.h"
#include "Config.h" #include "Config.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data Families::Families() {
{ }
Families::Families ()
{
}
Families::~Families () Families::~Families() {
{ }
}
void Families::LoadCertificate (const std::string& filename) void Families::LoadCertificate(const std::string &filename) {
{ SSL_CTX *ctx = SSL_CTX_new(TLS_method());
SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); int ret = SSL_CTX_use_certificate_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) {
if (ret) SSL *ssl = SSL_new(ctx);
{ X509 *cert = SSL_get_certificate(ssl);
SSL * ssl = SSL_new (ctx); if (cert) {
X509 * cert = SSL_get_certificate (ssl); std::shared_ptr<i2p::crypto::Verifier> verifier;
if (cert) // extract issuer name
{ char name[100];
std::shared_ptr<i2p::crypto::Verifier> verifier; X509_NAME_oneline(X509_get_issuer_name(cert), name, 100);
// extract issuer name char *cn = strstr(name, "CN=");
char name[100]; if (cn) {
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100); cn += 3;
char * cn = strstr (name, "CN="); char *family = strstr(cn, ".family");
if (cn) if (family) family[0] = 0;
{ }
cn += 3; auto pkey = X509_get_pubkey(cert);
char * family = strstr (cn, ".family"); int keyType = EVP_PKEY_base_id(pkey);
if (family) family[0] = 0; switch (keyType) {
} case EVP_PKEY_DSA:
auto pkey = X509_get_pubkey (cert); // TODO:
int keyType = EVP_PKEY_base_id (pkey); break;
switch (keyType) case EVP_PKEY_EC: {
{ EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey);
case EVP_PKEY_DSA: if (ecKey) {
// TODO: auto group = EC_KEY_get0_group(ecKey);
break; if (group) {
case EVP_PKEY_EC: int curve = EC_GROUP_get_curve_name(group);
{ if (curve == NID_X9_62_prime256v1) {
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); uint8_t signingKey[64];
if (ecKey) BIGNUM *x = BN_new(), *y = BN_new();
{ EC_POINT_get_affine_coordinates_GFp(group,
auto group = EC_KEY_get0_group (ecKey); EC_KEY_get0_public_key(ecKey), x, y, NULL);
if (group) i2p::crypto::bn2buf(x, signingKey, 32);
{ i2p::crypto::bn2buf(y, signingKey + 32, 32);
int curve = EC_GROUP_get_curve_name (group); BN_free(x);
if (curve == NID_X9_62_prime256v1) BN_free(y);
{ verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
uint8_t signingKey[64]; verifier->SetPublicKey(signingKey);
BIGNUM * x = BN_new(), * y = BN_new(); } else
EC_POINT_get_affine_coordinates_GFp (group, LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported");
EC_KEY_get0_public_key (ecKey), x, y, NULL); }
i2p::crypto::bn2buf (x, signingKey, 32); EC_KEY_free(ecKey);
i2p::crypto::bn2buf (y, signingKey + 32, 32); }
BN_free (x); BN_free (y); break;
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>(); }
verifier->SetPublicKey (signingKey); default:
} LogPrint(eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
else }
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); EVP_PKEY_free(pkey);
} if (verifier && cn)
EC_KEY_free (ecKey); m_SigningKeys.emplace(cn, std::make_pair(verifier, m_SigningKeys.size() + 1));
} }
break; SSL_free(ssl);
} } else
default: LogPrint(eLogError, "Family: Can't open certificate file ", filename);
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported"); SSL_CTX_free(ctx);
} }
EVP_PKEY_free (pkey);
if (verifier && cn)
m_SigningKeys.emplace (cn, std::make_pair(verifier, m_SigningKeys.size () + 1));
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Family: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
}
void Families::LoadCertificates () void Families::LoadCertificates() {
{ std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family";
std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family";
std::vector<std::string> files; std::vector<std::string> files;
int numCertificates = 0; int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) { if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir); LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir);
return; return;
} }
for (const std::string & file : files) { for (const std::string &file: files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) { if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Family: ignoring file ", file); LogPrint(eLogWarning, "Family: ignoring file ", file);
continue; continue;
} }
LoadCertificate (file); LoadCertificate(file);
numCertificates++; numCertificates++;
} }
LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded"); LogPrint(eLogInfo, "Family: ", numCertificates, " certificates loaded");
} }
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, bool Families::VerifyFamily(const std::string &family, const IdentHash &ident,
const char * signature, const char * key) const const char *signature, const char *key) const {
{ uint8_t buf[100], signatureBuf[64];
uint8_t buf[100], signatureBuf[64]; size_t len = family.length(), signatureLen = strlen(signature);
size_t len = family.length (), signatureLen = strlen (signature); if (len + 32 > 100) {
if (len + 32 > 100) LogPrint(eLogError, "Family: ", family, " is too long");
{ return false;
LogPrint (eLogError, "Family: ", family, " is too long"); }
return false;
}
memcpy (buf, family.c_str (), len); memcpy(buf, family.c_str(), len);
memcpy (buf + len, (const uint8_t *)ident, 32); memcpy(buf + len, (const uint8_t *) ident, 32);
len += 32; len += 32;
Base64ToByteStream (signature, signatureLen, signatureBuf, 64); Base64ToByteStream(signature, signatureLen, signatureBuf, 64);
auto it = m_SigningKeys.find (family); auto it = m_SigningKeys.find(family);
if (it != m_SigningKeys.end ()) if (it != m_SigningKeys.end())
return it->second.first->Verify (buf, len, signatureBuf); return it->second.first->Verify(buf, len, signatureBuf);
// TODO: process key // TODO: process key
return true; return true;
} }
FamilyID Families::GetFamilyID (const std::string& family) const FamilyID Families::GetFamilyID(const std::string &family) const {
{ auto it = m_SigningKeys.find(family);
auto it = m_SigningKeys.find (family); if (it != m_SigningKeys.end())
if (it != m_SigningKeys.end ()) return it->second.second;
return it->second.second; return 0;
return 0; }
}
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident) std::string CreateFamilySignature(const std::string &family, const IdentHash &ident) {
{ auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
auto filename = i2p::fs::DataDirPath("family", (family + ".key")); std::string sig;
std::string sig; SSL_CTX *ctx = SSL_CTX_new(TLS_method());
SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); int ret = SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) {
if (ret) SSL *ssl = SSL_new(ctx);
{ EVP_PKEY *pkey = SSL_get_privatekey(ssl);
SSL * ssl = SSL_new (ctx); EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey);
EVP_PKEY * pkey = SSL_get_privatekey (ssl); if (ecKey) {
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); auto group = EC_KEY_get0_group(ecKey);
if (ecKey) if (group) {
{ int curve = EC_GROUP_get_curve_name(group);
auto group = EC_KEY_get0_group (ecKey); if (curve == NID_X9_62_prime256v1) {
if (group) uint8_t signingPrivateKey[32], buf[50], signature[64];
{ i2p::crypto::bn2buf(EC_KEY_get0_private_key(ecKey), signingPrivateKey, 32);
int curve = EC_GROUP_get_curve_name (group); i2p::crypto::ECDSAP256Signer signer(signingPrivateKey);
if (curve == NID_X9_62_prime256v1) size_t len = family.length();
{ memcpy(buf, family.c_str(), len);
uint8_t signingPrivateKey[32], buf[50], signature[64]; memcpy(buf + len, (const uint8_t *) ident, 32);
i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32); len += 32;
i2p::crypto::ECDSAP256Signer signer (signingPrivateKey); signer.Sign(buf, len, signature);
size_t len = family.length (); len = Base64EncodingBufferSize(64);
memcpy (buf, family.c_str (), len); char *b64 = new char[len + 1];
memcpy (buf + len, (const uint8_t *)ident, 32); len = ByteStreamToBase64(signature, 64, b64, len);
len += 32; b64[len] = 0;
signer.Sign (buf, len, signature); sig = b64;
len = Base64EncodingBufferSize (64); delete[] b64;
char * b64 = new char[len+1]; } else
len = ByteStreamToBase64 (signature, 64, b64, len); LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported");
b64[len] = 0; }
sig = b64; }
delete[] b64; SSL_free(ssl);
} } else
else LogPrint(eLogError, "Family: Can't open keys file: ", filename);
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); SSL_CTX_free(ctx);
} return sig;
} }
SSL_free (ssl); }
}
else
LogPrint (eLogError, "Family: Can't open keys file: ", filename);
SSL_CTX_free (ctx);
return sig;
}
}
} }

View file

@ -15,34 +15,36 @@
#include "Signature.h" #include "Signature.h"
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data typedef int FamilyID;
{
typedef int FamilyID;
class Families
{
public:
Families (); class Families {
~Families (); public:
void LoadCertificates ();
bool VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key = nullptr) const;
FamilyID GetFamilyID (const std::string& family) const;
private: Families();
void LoadCertificate (const std::string& filename); ~Families();
private: void LoadCertificates();
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id) bool VerifyFamily(const std::string &family, const IdentHash &ident,
}; const char *signature, const char *key = nullptr) const;
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident); FamilyID GetFamilyID(const std::string &family) const;
// return base64 signature of empty string in case of failure
} private:
void LoadCertificate(const std::string &filename);
private:
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id)
};
std::string CreateFamilySignature(const std::string &family, const IdentHash &ident);
// return base64 signature of empty string in case of failure
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -22,291 +22,346 @@
#include "Queue.h" #include "Queue.h"
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace tunnel {
namespace tunnel class OutboundTunnel;
{ }
class OutboundTunnel;
}
namespace garlic namespace garlic {
{
enum GarlicDeliveryType enum GarlicDeliveryType {
{ eGarlicDeliveryTypeLocal = 0,
eGarlicDeliveryTypeLocal = 0, eGarlicDeliveryTypeDestination = 1,
eGarlicDeliveryTypeDestination = 1, eGarlicDeliveryTypeRouter = 2,
eGarlicDeliveryTypeRouter = 2, eGarlicDeliveryTypeTunnel = 3
eGarlicDeliveryTypeTunnel = 3 };
};
struct ElGamalBlock struct ElGamalBlock {
{ uint8_t sessionKey[32];
uint8_t sessionKey[32]; uint8_t preIV[32];
uint8_t preIV[32]; uint8_t padding[158];
uint8_t padding[158]; };
};
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
struct SessionTag : public i2p::data::Tag<32> {
SessionTag(const uint8_t *buf, uint32_t ts = 0) : Tag<32>(buf), creationTime(ts) {};
SessionTag() = default;
SessionTag(const SessionTag &) = default;
SessionTag &operator=(const SessionTag &) = default;
struct SessionTag: public i2p::data::Tag<32>
{
SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {};
SessionTag () = default;
SessionTag (const SessionTag& ) = default;
SessionTag& operator= (const SessionTag& ) = default;
#ifndef _WIN32 #ifndef _WIN32
SessionTag (SessionTag&& ) = default;
SessionTag& operator= (SessionTag&& ) = default; SessionTag(SessionTag &&) = default;
SessionTag &operator=(SessionTag &&) = default;
#endif #endif
uint32_t creationTime; // seconds since epoch uint32_t creationTime; // seconds since epoch
}; };
// AESDecryption is associated with session tags and store key // AESDecryption is associated with session tags and store key
class AESDecryption: public i2p::crypto::CBCDecryption class AESDecryption : public i2p::crypto::CBCDecryption {
{ public:
public:
AESDecryption (const uint8_t * key): m_Key (key) AESDecryption(const uint8_t *key) : m_Key(key) {
{ SetKey(key);
SetKey (key); }
}
const i2p::crypto::AESKey& GetKey () const { return m_Key; };
private: const i2p::crypto::AESKey &GetKey() const { return m_Key; };
i2p::crypto::AESKey m_Key; private:
};
struct GarlicRoutingPath i2p::crypto::AESKey m_Key;
{ };
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
int rtt; // RTT
uint32_t updateTime; // seconds since epoch
int numTimesUsed;
};
class GarlicDestination; struct GarlicRoutingPath {
class GarlicRoutingSession std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
{ std::shared_ptr<const i2p::data::Lease> remoteLease;
protected: int rtt; // RTT
uint32_t updateTime; // seconds since epoch
int numTimesUsed;
};
enum LeaseSetUpdateStatus class GarlicDestination;
{
eLeaseSetUpToDate = 0,
eLeaseSetUpdated,
eLeaseSetSubmitted,
eLeaseSetDoNotSend
};
public: class GarlicRoutingSession {
protected:
GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet); enum LeaseSetUpdateStatus {
GarlicRoutingSession (); eLeaseSetUpToDate = 0,
virtual ~GarlicRoutingSession (); eLeaseSetUpdated,
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0; eLeaseSetSubmitted,
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession eLeaseSetDoNotSend
virtual bool MessageConfirmed (uint32_t msgID); };
virtual bool IsRatchets () const { return false; };
virtual bool IsReadyToSend () const { return true; };
virtual bool IsTerminated () const { return !GetOwner (); };
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
void SetLeaseSetUpdated () public:
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
void CleanupUnconfirmedLeaseSet (uint64_t ts);
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath (); GarlicRoutingSession(GarlicDestination *owner, bool attachLeaseSet);
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
GarlicDestination * GetOwner () const { return m_Owner; } GarlicRoutingSession();
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
protected: virtual ~GarlicRoutingSession();
LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } virtual std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg) = 0;
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<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID); virtual bool CleanupUnconfirmedTags() { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed(uint32_t msgID);
private: virtual bool IsRatchets() const { return false; };
GarlicDestination * m_Owner; virtual bool IsReadyToSend() const { return true; };
LeaseSetUpdateStatus m_LeaseSetUpdateStatus; virtual bool IsTerminated() const { return !GetOwner(); };
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath; virtual uint64_t GetLastActivityTimestamp() const { return 0; }; // non-zero for rathets only
public: void SetLeaseSetUpdated() {
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
// for HTTP only bool IsLeaseSetNonConfirmed() const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
virtual size_t GetNumOutgoingTags () const { return 0; };
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession> bool IsLeaseSetUpdated() const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
{
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: uint64_t GetLeaseSetSubmissionTime() const { return m_LeaseSetSubmissionTime; }
ElGamalAESSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination, void CleanupUnconfirmedLeaseSet(uint64_t ts);
int numTags, bool attachLeaseSet);
ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~ElGamalAESSession () {};
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath();
bool MessageConfirmed (uint32_t msgID); void SetSharedRoutingPath(std::shared_ptr<GarlicRoutingPath> path);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
private: GarlicDestination *GetOwner() const { return m_Owner; }
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg); void SetOwner(GarlicDestination *owner) { m_Owner = owner; }
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
void TagsConfirmed (uint32_t msgID); protected:
UnconfirmedTags * GenerateSessionTags ();
private: LeaseSetUpdateStatus GetLeaseSetUpdateStatus() const { return m_LeaseSetUpdateStatus; }
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination; void SetLeaseSetUpdateStatus(LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; }
i2p::crypto::AESKey m_SessionKey; uint32_t GetLeaseSetUpdateMsgID() const { return m_LeaseSetUpdateMsgID; }
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
i2p::crypto::CBCEncryption m_Encryption; void SetLeaseSetUpdateMsgID(uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
public: void SetLeaseSetSubmissionTime(uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
// for HTTP only std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg(uint32_t msgID);
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
};
typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
class ECIESX25519AEADRatchetSession; private:
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
class ReceiveRatchetTagSet;
typedef std::shared_ptr<ReceiveRatchetTagSet> ReceiveRatchetTagSetPtr;
struct ECIESX25519AEADRatchetIndexTagset
{
int index;
ReceiveRatchetTagSetPtr tagset;
};
class GarlicDestination: public i2p::data::LocalDestination GarlicDestination *m_Owner;
{
public:
GarlicDestination (); LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
~GarlicDestination (); uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
void CleanUp (); std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
void SetNumTags (int numTags) { m_NumTags = numTags; };
int GetNumTags () const { return m_NumTags; };
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag public:
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
virtual void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
void RemoveECIESx25519Session (const uint8_t * staticKey);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
uint8_t * GetPayloadBuffer ();
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); // for HTTP only
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); virtual size_t GetNumOutgoingTags() const { return 0; };
virtual void SetLeaseSetUpdated (); };
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO //using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0; typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
protected: class ElGamalAESSession : public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession> {
struct UnconfirmedTags {
UnconfirmedTags(int n) : numTags(n), tagsCreationTime(0) { sessionTags = new SessionTag[numTags]; };
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag ~UnconfirmedTags() { delete[] sessionTags; };
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found uint32_t msgID;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only int numTags;
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0; SessionTag *sessionTags;
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg); uint32_t tagsCreationTime;
void HandleDeliveryStatusMessage (uint32_t msgID); };
void SaveTags (); public:
void LoadTags ();
private: ElGamalAESSession(GarlicDestination *owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption, ElGamalAESSession(const uint8_t *sessionKey, const SessionTag &sessionTag); // one time encryption
std::shared_ptr<i2p::tunnel::InboundTunnel> from); ~ElGamalAESSession() {};
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private: std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg);
// outgoing sessions bool MessageConfirmed(uint32_t msgID);
int m_NumTags;
std::mutex m_SessionsMutex;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
// incoming
int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public: bool CleanupExpiredTags(); // returns true if something left
bool CleanupUnconfirmedTags(); // returns true if something has been deleted
// for HTTP only private:
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 (); size_t CreateAESBlock(uint8_t *buf, std::shared_ptr<const I2NPMessage> msg);
} size_t
CreateGarlicPayload(uint8_t *payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags *newTags);
size_t CreateGarlicClove(uint8_t *buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove(uint8_t *buf, uint32_t msgID);
void TagsConfirmed(uint32_t msgID);
UnconfirmedTags *GenerateSessionTags();
private:
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
i2p::crypto::CBCEncryption m_Encryption;
public:
// for HTTP only
size_t GetNumOutgoingTags() const { return m_SessionTags.size(); };
};
typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
class ECIESX25519AEADRatchetSession;
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
class ReceiveRatchetTagSet;
typedef std::shared_ptr<ReceiveRatchetTagSet> ReceiveRatchetTagSetPtr;
struct ECIESX25519AEADRatchetIndexTagset {
int index;
ReceiveRatchetTagSetPtr tagset;
};
class GarlicDestination : public i2p::data::LocalDestination {
public:
GarlicDestination();
~GarlicDestination();
void CleanUp();
void SetNumTags(int numTags) { m_NumTags = numTags; };
int GetNumTags() const { return m_NumTags; };
void SetNumRatchetInboundTags(int numTags) { m_NumRatchetInboundTags = numTags; };
int GetNumRatchetInboundTags() const { return m_NumRatchetInboundTags; };
std::shared_ptr<GarlicRoutingSession>
GetRoutingSession(std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags();
void RemoveDeliveryStatusSession(uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessageForRouter(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg);
void AddSessionKey(const uint8_t *key, const uint8_t *tag); // one tag
void AddECIESx25519Key(const uint8_t *key, uint64_t tag); // one tag
virtual bool SubmitSessionKey(const uint8_t *key, const uint8_t *tag); // from different thread
virtual void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag); // from different thread
void DeliveryStatusSent(GarlicRoutingSessionPtr session, uint32_t msgID);
uint64_t AddECIESx25519SessionNextTag(ReceiveRatchetTagSetPtr tagset);
void AddECIESx25519Session(const uint8_t *staticKey, ECIESX25519AEADRatchetSessionPtr session);
void RemoveECIESx25519Session(const uint8_t *staticKey);
void HandleECIESx25519GarlicClove(const uint8_t *buf, size_t len);
uint8_t *GetPayloadBuffer();
virtual void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated();
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet() = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const = 0;
protected:
void AddECIESx25519Key(const uint8_t *key, const uint8_t *tag); // one tag
bool HandleECIESx25519TagMessage(uint8_t *buf, size_t len); // return true if found
virtual void HandleI2NPMessage(const uint8_t *buf, size_t len) = 0; // called from clove only
virtual bool
HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID) = 0;
void HandleGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage(uint32_t msgID);
void SaveTags();
void LoadTags();
private:
void HandleAESBlock(uint8_t *buf, size_t len, std::shared_ptr<AESDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload(uint8_t *buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
// outgoing sessions
int m_NumTags;
std::mutex m_SessionsMutex;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
uint8_t *m_PayloadBuffer; // for ECIESX25519AEADRatchet
// incoming
int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public:
// 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();
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -12,59 +12,70 @@
#include <memory> #include <memory>
#include <openssl/ec.h> #include <openssl/ec.h>
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
// ГОСТ Р 34.10 // ГОСТ Р 34.10
enum GOSTR3410ParamSet enum GOSTR3410ParamSet {
{ eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1
eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1 // XchA = A, XchB = C
// XchA = A, XchB = C //eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0
//eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0 //eGOSTR3410CryptoProXchB, // 1.2.643.2.2.36.1
//eGOSTR3410CryptoProXchB, // 1.2.643.2.2.36.1 eGOSTR3410TC26A512, // 1.2.643.7.1.2.1.2.1
eGOSTR3410TC26A512, // 1.2.643.7.1.2.1.2.1 eGOSTR3410NumParamSets
eGOSTR3410NumParamSets };
};
class GOSTR3410Curve class GOSTR3410Curve {
{ public:
public:
GOSTR3410Curve (BIGNUM * a, BIGNUM * b, BIGNUM * p, BIGNUM * q, BIGNUM * x, BIGNUM * y); GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y);
~GOSTR3410Curve ();
size_t GetKeyLen () const { return m_KeyLen; }; ~GOSTR3410Curve();
const EC_GROUP * GetGroup () const { return m_Group; };
EC_POINT * MulP (const BIGNUM * n) const;
bool GetXY (const EC_POINT * p, BIGNUM * x, BIGNUM * y) const;
EC_POINT * CreatePoint (const BIGNUM * x, const BIGNUM * y) const;
void Sign (const BIGNUM * priv, const BIGNUM * digest, BIGNUM * r, BIGNUM * s);
bool Verify (const EC_POINT * pub, const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s);
EC_POINT * RecoverPublicKey (const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s, bool isNegativeY = false) const;
private: size_t GetKeyLen() const { return m_KeyLen; };
EC_GROUP * m_Group; const EC_GROUP *GetGroup() const { return m_Group; };
size_t m_KeyLen; // in bytes
};
std::unique_ptr<GOSTR3410Curve>& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet); EC_POINT *MulP(const BIGNUM *n) const;
bool GetXY(const EC_POINT *p, BIGNUM *x, BIGNUM *y) const;
EC_POINT *CreatePoint(const BIGNUM *x, const BIGNUM *y) const;
void Sign(const BIGNUM *priv, const BIGNUM *digest, BIGNUM *r, BIGNUM *s);
bool Verify(const EC_POINT *pub, const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s);
EC_POINT *
RecoverPublicKey(const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s, bool isNegativeY = false) const;
private:
EC_GROUP *m_Group;
size_t m_KeyLen; // in bytes
};
std::unique_ptr <GOSTR3410Curve> &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet);
// Big Endian // Big Endian
void GOSTR3411_2012_256 (const uint8_t * buf, size_t len, uint8_t * digest); void GOSTR3411_2012_256(const uint8_t *buf, size_t len, uint8_t *digest);
void GOSTR3411_2012_512 (const uint8_t * buf, size_t len, uint8_t * digest);
void GOSTR3411_2012_512(const uint8_t *buf, size_t len, uint8_t *digest);
// Little Endian // Little Endian
struct GOSTR3411_2012_CTX; struct GOSTR3411_2012_CTX;
GOSTR3411_2012_CTX * GOSTR3411_2012_CTX_new ();
void GOSTR3411_2012_CTX_Init (GOSTR3411_2012_CTX * ctx, bool is512 = true); GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new();
void GOSTR3411_2012_CTX_Update (const uint8_t * buf, size_t len, GOSTR3411_2012_CTX * ctx);
void GOSTR3411_2012_CTX_Finish (uint8_t * digest, GOSTR3411_2012_CTX * ctx); void GOSTR3411_2012_CTX_Init(GOSTR3411_2012_CTX *ctx, bool is512 = true);
void GOSTR3411_2012_CTX_free (GOSTR3411_2012_CTX * ctx);
} void GOSTR3411_2012_CTX_Update(const uint8_t *buf, size_t len, GOSTR3411_2012_CTX *ctx);
void GOSTR3411_2012_CTX_Finish(uint8_t *digest, GOSTR3411_2012_CTX *ctx);
void GOSTR3411_2012_CTX_free(GOSTR3411_2012_CTX *ctx);
}
} }
#endif #endif

View file

@ -13,189 +13,166 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Gzip.h" #include "Gzip.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data const size_t GZIP_CHUNK_SIZE = 16384;
{
const size_t GZIP_CHUNK_SIZE = 16384;
GzipInflator::GzipInflator (): m_IsDirty (false) GzipInflator::GzipInflator() : m_IsDirty(false) {
{ memset(&m_Inflator, 0, sizeof(m_Inflator));
memset (&m_Inflator, 0, sizeof (m_Inflator)); inflateInit2(&m_Inflator, MAX_WBITS + 16); // gzip
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip }
}
GzipInflator::~GzipInflator () GzipInflator::~GzipInflator() {
{ inflateEnd(&m_Inflator);
inflateEnd (&m_Inflator); }
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) size_t GzipInflator::Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen) {
{ if (inLen < 23) return 0;
if (inLen < 23) return 0; if (in[10] == 0x01) // non compressed
if (in[10] == 0x01) // non compressed {
{ size_t len = bufle16toh(in + 11);
size_t len = bufle16toh (in + 11); if (len + 23 < inLen) {
if (len + 23 < inLen) LogPrint(eLogError, "Gzip: Incorrect length");
{ return 0;
LogPrint (eLogError, "Gzip: Incorrect length"); }
return 0; if (len > outLen) len = outLen;
} memcpy(out, in + 15, len);
if (len > outLen) len = outLen; return len;
memcpy (out, in + 15, len); } else {
return len; if (m_IsDirty) inflateReset(&m_Inflator);
} m_IsDirty = true;
else m_Inflator.next_in = const_cast<uint8_t *>(in);
{ m_Inflator.avail_in = inLen;
if (m_IsDirty) inflateReset (&m_Inflator); m_Inflator.next_out = out;
m_IsDirty = true; m_Inflator.avail_out = outLen;
m_Inflator.next_in = const_cast<uint8_t *>(in); int err;
m_Inflator.avail_in = inLen; if ((err = inflate(&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
m_Inflator.next_out = out; return outLen - m_Inflator.avail_out;
m_Inflator.avail_out = outLen; // else
int err; LogPrint(eLogError, "Gzip: Inflate error ", err);
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) return 0;
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) void GzipInflator::Inflate(const uint8_t *in, size_t inLen, std::ostream &os) {
{ m_IsDirty = true;
m_IsDirty = true; uint8_t *out = new uint8_t[GZIP_CHUNK_SIZE];
uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE]; m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.next_in = const_cast<uint8_t *>(in); m_Inflator.avail_in = inLen;
m_Inflator.avail_in = inLen; int ret;
int ret; do {
do m_Inflator.next_out = out;
{ m_Inflator.avail_out = GZIP_CHUNK_SIZE;
m_Inflator.next_out = out; ret = inflate(&m_Inflator, Z_NO_FLUSH);
m_Inflator.avail_out = GZIP_CHUNK_SIZE; if (ret < 0) {
ret = inflate (&m_Inflator, Z_NO_FLUSH); inflateEnd(&m_Inflator);
if (ret < 0) os.setstate(std::ios_base::failbit);
{ break;
inflateEnd (&m_Inflator); }
os.setstate(std::ios_base::failbit); os.write((char *) out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
break; } while (!m_Inflator.avail_out); // more data to read
} delete[] out;
os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out); }
}
while (!m_Inflator.avail_out); // more data to read
delete[] out;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out) void GzipInflator::Inflate(std::istream &in, std::ostream &out) {
{ uint8_t *buf = new uint8_t[GZIP_CHUNK_SIZE];
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE]; while (!in.eof()) {
while (!in.eof ()) in.read((char *) buf, GZIP_CHUNK_SIZE);
{ Inflate(buf, in.gcount(), out);
in.read ((char *) buf, GZIP_CHUNK_SIZE); }
Inflate (buf, in.gcount (), out); delete[] buf;
} }
delete[] buf;
}
GzipDeflator::GzipDeflator (): m_IsDirty (false) GzipDeflator::GzipDeflator() : m_IsDirty(false) {
{ memset(&m_Deflator, 0, sizeof(m_Deflator));
memset (&m_Deflator, 0, sizeof (m_Deflator)); deflateInit2(&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8,
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
} }
GzipDeflator::~GzipDeflator () GzipDeflator::~GzipDeflator() {
{ deflateEnd(&m_Deflator);
deflateEnd (&m_Deflator); }
}
void GzipDeflator::SetCompressionLevel (int level) void GzipDeflator::SetCompressionLevel(int level) {
{ deflateParams(&m_Deflator, level, Z_DEFAULT_STRATEGY);
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY); }
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) size_t GzipDeflator::Deflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen) {
{ if (m_IsDirty) deflateReset(&m_Deflator);
if (m_IsDirty) deflateReset (&m_Deflator); m_IsDirty = true;
m_IsDirty = true; m_Deflator.next_in = const_cast<uint8_t *>(in);
m_Deflator.next_in = const_cast<uint8_t *>(in); m_Deflator.avail_in = inLen;
m_Deflator.avail_in = inLen; m_Deflator.next_out = out;
m_Deflator.next_out = out; m_Deflator.avail_out = outLen;
m_Deflator.avail_out = outLen; int err;
int err; if ((err = deflate(&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) out[9] = 0xff; // OS is always unknown
{ return outLen - m_Deflator.avail_out;
out[9] = 0xff; // OS is always unknown }
return outLen - m_Deflator.avail_out; // else
} LogPrint(eLogError, "Gzip: Deflate error ", err);
// else return 0;
LogPrint (eLogError, "Gzip: Deflate error ", err); }
return 0;
}
size_t GzipDeflator::Deflate (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen) size_t GzipDeflator::Deflate(const std::vector <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out,
{ size_t outLen) {
if (m_IsDirty) deflateReset (&m_Deflator); if (m_IsDirty) deflateReset(&m_Deflator);
m_IsDirty = true; m_IsDirty = true;
size_t offset = 0; size_t offset = 0;
int err; int err;
for (const auto& it: bufs) for (const auto &it: bufs) {
{ m_Deflator.next_in = const_cast<uint8_t *>(it.first);
m_Deflator.next_in = const_cast<uint8_t *>(it.first); m_Deflator.avail_in = it.second;
m_Deflator.avail_in = it.second; m_Deflator.next_out = out + offset;
m_Deflator.next_out = out + offset; m_Deflator.avail_out = outLen - offset;
m_Deflator.avail_out = outLen - offset; auto flush = (it == bufs.back()) ? Z_FINISH : Z_NO_FLUSH;
auto flush = (it == bufs.back ()) ? Z_FINISH : Z_NO_FLUSH; err = deflate(&m_Deflator, flush);
err = deflate (&m_Deflator, flush); if (err) {
if (err) if (flush && err == Z_STREAM_END) {
{ out[9] = 0xff; // OS is always unknown
if (flush && err == Z_STREAM_END) return outLen - m_Deflator.avail_out;
{ }
out[9] = 0xff; // OS is always unknown break;
return outLen - m_Deflator.avail_out; }
} offset = outLen - m_Deflator.avail_out;
break; }
} // else
offset = outLen - m_Deflator.avail_out; LogPrint(eLogError, "Gzip: Deflate error ", err);
} return 0;
// 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) 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};
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;
if (outLen < (size_t)inLen + 23) return 0; memcpy(out, gzipHeader, 11);
memcpy (out, gzipHeader, 11); htole16buf(out + 11, inLen);
htole16buf (out + 11, inLen); htole16buf(out + 13, 0xffff - inLen);
htole16buf (out + 13, 0xffff - inLen); memcpy(out + 15, in, inLen);
memcpy (out + 15, in, inLen); htole32buf(out + inLen + 15, crc32(0, in, inLen));
htole32buf (out + inLen + 15, crc32 (0, in, inLen)); htole32buf(out + inLen + 19, inLen);
htole32buf (out + inLen + 19, inLen); return inLen + 23;
return inLen + 23; }
}
size_t GzipNoCompression (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen) size_t
{ GzipNoCompression(const std::vector <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out, size_t outLen) {
static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 }; static const uint8_t gzipHeader[11] = {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01};
memcpy (out, gzipHeader, 11); memcpy(out, gzipHeader, 11);
uint32_t crc = 0; uint32_t crc = 0;
size_t len = 0, len1; size_t len = 0, len1;
for (const auto& it: bufs) for (const auto &it: bufs) {
{ len1 = len;
len1 = len; len += it.second;
len += it.second; if (outLen < len + 23) return 0;
if (outLen < len + 23) return 0; memcpy(out + 15 + len1, it.first, it.second);
memcpy (out + 15 + len1, it.first, it.second); crc = crc32(crc, it.first, it.second);
crc = crc32 (crc, it.first, it.second); }
} if (len > 0xffff) return 0;
if (len > 0xffff) return 0; htole32buf(out + len + 15, crc);
htole32buf (out + len + 15, crc); htole32buf(out + len + 19, len);
htole32buf (out + len + 19, len); htole16buf(out + 11, len);
htole16buf (out + 11, len); htole16buf(out + 13, 0xffff - len);
htole16buf (out + 13, 0xffff - len); return len + 23;
return len + 23; }
}
} // data } // data
} // i2p } // i2p

View file

@ -12,48 +12,51 @@
#include <zlib.h> #include <zlib.h>
#include <vector> #include <vector>
namespace i2p namespace i2p {
{ namespace data {
namespace data class GzipInflator {
{ public:
class GzipInflator
{
public:
GzipInflator (); GzipInflator();
~GzipInflator ();
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); ~GzipInflator();
/** @note @a os failbit will be set in case of error */
void Inflate (const uint8_t * in, size_t inLen, std::ostream& os);
void Inflate (std::istream& in, std::ostream& out);
private: size_t Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen);
z_stream m_Inflator; /** @note @a os failbit will be set in case of error */
bool m_IsDirty; void Inflate(const uint8_t *in, size_t inLen, std::ostream &os);
};
class GzipDeflator void Inflate(std::istream &in, std::ostream &out);
{
public:
GzipDeflator (); private:
~GzipDeflator ();
void SetCompressionLevel (int level); z_stream m_Inflator;
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); bool m_IsDirty;
size_t Deflate (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen); };
private: class GzipDeflator {
public:
z_stream m_Deflator; GzipDeflator();
bool m_IsDirty;
};
size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen); // for < 64K ~GzipDeflator();
size_t GzipNoCompression (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen); // for total size < 64K
} // 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 <std::pair<const uint8_t *, size_t>> &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 <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out,
size_t outLen); // for total size < 64K
} // data
} // i2p } // i2p
#endif /* GZIP_H__ */ #endif /* GZIP_H__ */

File diff suppressed because it is too large Load diff

View file

@ -16,160 +16,165 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace i2p namespace i2p {
{ namespace http {
namespace http const char CRLF[] = "\r\n"; /**< HTTP line terminator */
{ const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
const char CRLF[] = "\r\n"; /**< HTTP line terminator */ extern const std::vector <std::string> HTTP_METHODS; /**< list of valid HTTP methods */
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */ extern const std::vector <std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
extern const std::vector<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
struct URL struct URL {
{ std::string schema;
std::string schema; std::string user;
std::string user; std::string pass;
std::string pass; std::string host;
std::string host; unsigned short int port;
unsigned short int port; std::string path;
std::string path; std::string query;
std::string query; std::string frag;
std::string frag;
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {}; URL() : schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
/** /**
* @brief Tries to parse url from string * @brief Tries to parse url from string
* @return true on success, false on invalid url * @return true on success, false on invalid url
*/ */
bool parse (const char *str, std::size_t len = 0); bool parse(const char *str, std::size_t len = 0);
bool parse (const std::string& url);
/** bool parse(const std::string &url);
* @brief Parse query part of url to key/value map
* @note Honestly, this should be implemented with std::multimap
*/
bool parse_query(std::map<std::string, std::string> & params);
/** /**
* @brief Serialize URL structure to url * @brief Parse query part of url to key/value map
* @note Returns relative url if schema if empty, absolute url otherwise * @note Honestly, this should be implemented with std::multimap
*/ */
std::string to_string (); bool parse_query(std::map <std::string, std::string> &params);
/** /**
* @brief return true if the host is inside i2p * @brief Serialize URL structure to url
*/ * @note Returns relative url if schema if empty, absolute url otherwise
bool is_i2p() const; */
}; std::string to_string();
struct HTTPMsg /**
{ * @brief return true if the host is inside i2p
std::map<std::string, std::string> headers; */
bool is_i2p() const;
};
void add_header(const char *name, std::string & value, bool replace = false); struct HTTPMsg {
void add_header(const char *name, const char *value, bool replace = false); std::map <std::string, std::string> headers;
void del_header(const char *name);
/** @brief Returns declared message length or -1 if unknown */ void add_header(const char *name, std::string &value, bool replace = false);
long int content_length() const;
};
struct HTTPReq void add_header(const char *name, const char *value, bool replace = false);
{
std::list<std::pair<std::string, std::string> > headers;
std::string version;
std::string method;
std::string uri;
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {}; void del_header(const char *name);
/** /** @brief Returns declared message length or -1 if unknown */
* @brief Tries to parse HTTP request from string long int content_length() const;
* @return -1 on error, 0 on incomplete query, >0 on success };
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/** @brief Serialize HTTP request to string */ struct HTTPReq {
std::string to_string(); std::list <std::pair<std::string, std::string>> headers;
void write(std::ostream & o); std::string version;
std::string method;
std::string uri;
void AddHeader (const std::string& name, const std::string& value); HTTPReq() : version("HTTP/1.0"), method("GET"), uri("/") {};
void UpdateHeader (const std::string& name, const std::string& value);
void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt
void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); };
std::string GetHeader (const std::string& name) const;
};
struct HTTPRes : HTTPMsg { /**
std::string version; * @brief Tries to parse HTTP request from string
std::string status; * @return -1 on error, 0 on incomplete query, >0 on success
unsigned short int code; * @note Positive return value is a size of header
/** */
* @brief Simplifies response generation int parse(const char *buf, size_t len);
*
* If this variable is set, on @a to_string() call:
* * Content-Length header will be added if missing,
* * contents of @a body will be included in generated response
*/
std::string body;
HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {} int parse(const std::string &buf);
/** /** @brief Serialize HTTP request to string */
* @brief Tries to parse HTTP response from string std::string to_string();
* @return -1 on error, 0 on incomplete query, >0 on success
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/** void write(std::ostream &o);
* @brief Serialize HTTP response to string
* @note If @a version is set to HTTP/1.1, and Date header is missing,
* it will be generated based on current time and added to headers
* @note If @a body is set and Content-Length header is missing,
* this header will be added, based on body's length
*/
std::string to_string();
void write(std::ostream & o); void AddHeader(const std::string &name, const std::string &value);
/** @brief Checks that response declared as chunked data */ void UpdateHeader(const std::string &name, const std::string &value);
bool is_chunked() const ;
/** @brief Checks that response contains compressed data */ void RemoveHeader(const std::string &name,
bool is_gzipped(bool includingI2PGzip = true) const; const std::string &exempt); // remove all headers starting with name, but exempt
}; void RemoveHeader(const std::string &name) { RemoveHeader(name, ""); };
/** std::string GetHeader(const std::string &name) const;
* @brief returns HTTP status string by integer code };
* @param code HTTP code [100, 599]
* @return Immutable string with status
*/
const char * HTTPCodeToStatus(int code);
/** struct HTTPRes : HTTPMsg {
* @brief Replaces %-encoded characters in string with their values std::string version;
* @param data Source string std::string status;
* @param null If set to true - decode also %00 sequence, otherwise - skip unsigned short int code;
* @return Decoded string /**
*/ * @brief Simplifies response generation
std::string UrlDecode(const std::string& data, bool null = false); *
* If this variable is set, on @a to_string() call:
* * Content-Length header will be added if missing,
* * contents of @a body will be included in generated response
*/
std::string body;
/** HTTPRes() : version("HTTP/1.1"), status("OK"), code(200) {}
* @brief Merge HTTP response content with Transfer-Encoding: chunked
* @param in Input stream
* @param out Output stream
* @return true on success, false otherwise
*/
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass); /**
* @brief Tries to parse HTTP response from string
* @return -1 on error, 0 on incomplete query, >0 on success
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
} // http int parse(const std::string &buf);
/**
* @brief Serialize HTTP response to string
* @note If @a version is set to HTTP/1.1, and Date header is missing,
* it will be generated based on current time and added to headers
* @note If @a body is set and Content-Length header is missing,
* this header will be added, based on body's length
*/
std::string to_string();
void write(std::ostream &o);
/** @brief Checks that response declared as chunked data */
bool is_chunked() const;
/** @brief Checks that response contains compressed data */
bool is_gzipped(bool includingI2PGzip = true) const;
};
/**
* @brief returns HTTP status string by integer code
* @param code HTTP code [100, 599]
* @return Immutable string with status
*/
const char *HTTPCodeToStatus(int code);
/**
* @brief Replaces %-encoded characters in string with their values
* @param data Source string
* @param null If set to true - decode also %00 sequence, otherwise - skip
* @return Decoded string
*/
std::string UrlDecode(const std::string &data, bool null = false);
/**
* @brief Merge HTTP response content with Transfer-Encoding: chunked
* @param in Input stream
* @param out Output stream
* @return true on success, false otherwise
*/
bool MergeChunkedResponse(std::istream &in, std::ostream &out);
std::string CreateBasicAuthorizationString(const std::string &user, const std::string &pass);
} // http
} // i2p } // i2p
#endif /* HTTP_H__ */ #endif /* HTTP_H__ */

File diff suppressed because it is too large Load diff

View file

@ -19,299 +19,350 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "LeaseSet.h" #include "LeaseSet.h"
namespace i2p namespace i2p {
{ // I2NP header
// I2NP header const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_HEADER_TYPEID_OFFSET = 0; const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1; const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4;
const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4; const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8;
const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8; const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2;
const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2; const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1;
const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1;
// I2NP short header // I2NP short header
const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0; const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1; const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4;
// I2NP NTCP2 header // 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 // Tunnel Gateway header
const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0;
const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4;
const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2; const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2;
// DeliveryStatus // DeliveryStatus
const size_t DELIVERY_STATUS_MSGID_OFFSET = 0; const size_t DELIVERY_STATUS_MSGID_OFFSET = 0;
const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4; const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
// DatabaseStore // DatabaseStore
const size_t DATABASE_STORE_KEY_OFFSET = 0; const size_t DATABASE_STORE_KEY_OFFSET = 0;
const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32; const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32;
const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1; const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1;
const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4; const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4;
// TunnelBuild // TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528; const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218; const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218;
// BuildRequestRecordEncrypted // BuildRequestRecordEncrypted
const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0; const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16; const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
// ECIES BuildRequestRecordClearText // ECIES BuildRequestRecordClearText
const size_t ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; const size_t ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; const size_t ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32; const size_t ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32; const size_t ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32; const size_t ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
const size_t ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16; const size_t ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET = ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET + 1; const size_t ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET = ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET + 3; const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET + 3;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET =
const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4; ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET = ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET =
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464; ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET = ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464;
// ECIES BuildResponseRecord // ECIES BuildResponseRecord
const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511;
// ShortRequestRecordClearText // ShortRequestRecordClearText
const size_t SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET = 16; const size_t SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET = 16;
const size_t SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; const size_t SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET = SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET = SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_FLAG_OFFSET = SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; const size_t SHORT_REQUEST_RECORD_FLAG_OFFSET = SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET = SHORT_REQUEST_RECORD_FLAG_OFFSET + 1; const size_t SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET = SHORT_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE = SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET + 2; const size_t SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE = SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET + 2;
const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1; const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1;
const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154; const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154;
// ShortResponseRecord // ShortResponseRecord
const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201;
enum I2NPMessageType enum I2NPMessageType {
{ eI2NPDummyMsg = 0,
eI2NPDummyMsg = 0, eI2NPDatabaseStore = 1,
eI2NPDatabaseStore = 1, eI2NPDatabaseLookup = 2,
eI2NPDatabaseLookup = 2, eI2NPDatabaseSearchReply = 3,
eI2NPDatabaseSearchReply = 3, eI2NPDeliveryStatus = 10,
eI2NPDeliveryStatus = 10, eI2NPGarlic = 11,
eI2NPGarlic = 11, eI2NPTunnelData = 18,
eI2NPTunnelData = 18, eI2NPTunnelGateway = 19,
eI2NPTunnelGateway = 19, eI2NPData = 20,
eI2NPData = 20, eI2NPTunnelBuild = 21,
eI2NPTunnelBuild = 21, eI2NPTunnelBuildReply = 22,
eI2NPTunnelBuildReply = 22, eI2NPVariableTunnelBuild = 23,
eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuildReply = 24,
eI2NPVariableTunnelBuildReply = 24, eI2NPShortTunnelBuild = 25,
eI2NPShortTunnelBuild = 25, eI2NPShortTunnelBuildReply = 26
eI2NPShortTunnelBuildReply = 26 };
};
const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80;
const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40; const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40;
const int NUM_TUNNEL_BUILD_RECORDS = 8; const int NUM_TUNNEL_BUILD_RECORDS = 8;
// DatabaseLookup flags // DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02; const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10; const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0; const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100 const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000 const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100 const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
namespace tunnel namespace tunnel {
{ class InboundTunnel;
class InboundTunnel;
class TunnelPool;
}
const size_t I2NP_MAX_MESSAGE_SIZE = 62708; class TunnelPool;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096; }
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds
struct I2NPMessage const size_t I2NP_MAX_MESSAGE_SIZE = 62708;
{ const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
uint8_t * buf; const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
size_t len, offset, maxLen; const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60 * 1000; // 1 minute in milliseconds
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), struct I2NPMessage {
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header uint8_t *buf;
size_t len, offset, maxLen;
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
// header accessors I2NPMessage() : buf(nullptr), len(I2NP_HEADER_SIZE + 2),
uint8_t * GetHeader () { return GetBuffer (); }; offset(2), maxLen(0), from(nullptr) {}; // reserve 2 bytes for NTCP header
const uint8_t * GetHeader () const { return GetBuffer (); };
void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; };
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); };
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize () { SetSize (GetPayloadLength ()); };
void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; };
void UpdateChks ()
{
uint8_t hash[32];
SHA256(GetPayload (), GetPayloadLength (), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
// payload // header accessors
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; }; uint8_t *GetHeader() { return GetBuffer(); };
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { return buf + offset; };
size_t GetLength () const { return len - offset; };
size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; };
void Align (size_t alignment) const uint8_t *GetHeader() const { return GetBuffer(); };
{
if (len + alignment > maxLen) return;
size_t rem = ((size_t)GetBuffer ()) % alignment;
if (rem)
{
offset += (alignment - rem);
len += (alignment - rem);
}
}
size_t Concat (const uint8_t * buf1, size_t len1) void SetTypeID(uint8_t typeID) { GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
{
// make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len;
memcpy (buf + len, buf1, len1);
len += len1;
return len1;
}
I2NPMessage& operator=(const I2NPMessage& other) uint8_t GetTypeID() const { return GetHeader()[I2NP_HEADER_TYPEID_OFFSET]; };
{
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
len = offset + other.GetLength ();
from = other.from;
return *this;
}
// for SSU only void SetMsgID(uint32_t msgID) { htobe32buf(GetHeader() + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
{
const uint8_t * ssu = GetSSUHeader ();
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID (msgID);
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
}
uint32_t ToSSU () // return msgID
{
uint8_t header[I2NP_HEADER_SIZE];
memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
uint8_t * ssu = GetSSUHeader ();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
}
// for NTCP2 only
uint8_t * GetNTCP2Header () { return GetPayload () - I2NP_NTCP2_HEADER_SIZE; };
size_t GetNTCP2Length () const { return GetPayloadLength () + I2NP_NTCP2_HEADER_SIZE; };
void FromNTCP2 ()
{
const uint8_t * ntcp2 = GetNTCP2Header ();
memcpy (GetHeader () + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
SetExpiration (bufbe32toh (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET)*1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
}
void ToNTCP2 () uint32_t GetMsgID() const { return bufbe32toh(GetHeader() + I2NP_HEADER_MSGID_OFFSET); };
{
uint8_t * ntcp2 = GetNTCP2Header ();
htobe32buf (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET, bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
memcpy (ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader () + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
}
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true); void SetExpiration(uint64_t expiration) {
void RenewI2NPMessageHeader (); htobe64buf(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET, expiration);
bool IsExpired () const; };
};
template<int sz> uint64_t GetExpiration() const { return bufbe64toh(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET); };
struct I2NPMessageBuffer: public I2NPMessage
{
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
};
std::shared_ptr<I2NPMessage> NewI2NPMessage (); void SetSize(uint16_t size) { htobe16buf(GetHeader() + I2NP_HEADER_SIZE_OFFSET, size); };
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); uint16_t GetSize() const { return bufbe16toh(GetHeader() + I2NP_HEADER_SIZE_OFFSET); };
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID); void UpdateSize() { SetSize(GetPayloadLength()); };
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr); void SetChks(uint8_t chks) { GetHeader()[I2NP_HEADER_CHKS_OFFSET] = chks; };
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf); void UpdateChks() {
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); uint8_t hash[32];
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint); SHA256(GetPayload(), GetPayloadLength(), hash);
GetHeader()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); // payload
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, uint8_t *GetPayload() { return GetBuffer() + I2NP_HEADER_SIZE; };
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len); const uint8_t *GetPayload() const { return GetBuffer() + I2NP_HEADER_SIZE; };
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler uint8_t *GetBuffer() { return buf + offset; };
{
public:
~I2NPMessagesHandler (); const uint8_t *GetBuffer() const { return buf + offset; };
void PutNextMessage (std::shared_ptr<I2NPMessage>&& msg);
void Flush ();
private: size_t GetLength() const { return len - offset; };
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs; size_t GetPayloadLength() const { return GetLength() - I2NP_HEADER_SIZE; };
};
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500; void Align(size_t alignment) {
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels); if (len + alignment > maxLen) return;
uint16_t GetMaxNumTransitTunnels (); size_t rem = ((size_t) GetBuffer()) % alignment;
if (rem) {
offset += (alignment - rem);
len += (alignment - rem);
}
}
size_t Concat(const uint8_t *buf1, size_t len1) {
// make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len;
memcpy(buf + len, buf1, len1);
len += len1;
return len1;
}
I2NPMessage &operator=(const I2NPMessage &other) {
memcpy(buf + offset, other.buf + other.offset, other.GetLength());
len = offset + other.GetLength();
from = other.from;
return *this;
}
// for SSU only
uint8_t *GetSSUHeader() { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU(uint32_t msgID) // we have received SSU message and convert it to regular
{
const uint8_t *ssu = GetSSUHeader();
GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID(msgID);
SetExpiration(bufbe32toh(ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET) * 1000LL);
SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks(0);
}
uint32_t ToSSU() // return msgID
{
uint8_t header[I2NP_HEADER_SIZE];
memcpy(header, GetHeader(), I2NP_HEADER_SIZE);
uint8_t *ssu = GetSSUHeader();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf(ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET,
bufbe64toh(header + I2NP_HEADER_EXPIRATION_OFFSET) / 1000LL);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh(header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh(header + I2NP_HEADER_MSGID_OFFSET);
}
// for NTCP2 only
uint8_t *GetNTCP2Header() { return GetPayload() - I2NP_NTCP2_HEADER_SIZE; };
size_t GetNTCP2Length() const { return GetPayloadLength() + I2NP_NTCP2_HEADER_SIZE; };
void FromNTCP2() {
const uint8_t *ntcp2 = GetNTCP2Header();
memcpy(GetHeader() + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
SetExpiration(bufbe32toh(ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET) * 1000LL);
SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks(0);
}
void ToNTCP2() {
uint8_t *ntcp2 = GetNTCP2Header();
htobe32buf(ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET,
bufbe64toh(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET) / 1000LL);
memcpy(ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader() + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
}
void FillI2NPMessageHeader(I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true);
void RenewI2NPMessageHeader();
bool IsExpired() const;
};
template<int sz>
struct I2NPMessageBuffer : public I2NPMessage {
I2NPMessageBuffer() {
buf = m_Buffer;
maxLen = sz;
};
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
};
std::shared_ptr<I2NPMessage> NewI2NPMessage();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage(bool endpoint);
std::shared_ptr<I2NPMessage> NewI2NPMessage(size_t len);
std::shared_ptr<I2NPMessage>
CreateI2NPMessage(I2NPMessageType msgType, const uint8_t *buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage>
CreateI2NPMessage(const uint8_t *buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CopyI2NPMessage(std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg(uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg(const uint8_t *key, const uint8_t *from,
uint32_t replyTunnelID, bool exploratory = false,
std::set<i2p::data::IdentHash> *excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg(const i2p::data::IdentHash &dest,
const std::set<i2p::data::IdentHash> &excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t *replyKey, const uint8_t *replyTag,
bool replyECIES = false);
std::shared_ptr<I2NPMessage>
CreateDatabaseSearchReply(const i2p::data::IdentHash &ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage>
CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg(const i2p::data::IdentHash &storeHash,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage>
CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg(std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg(const uint8_t *buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg(uint32_t tunnelID, const uint8_t *payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg(bool endpoint);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, const uint8_t *buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t *buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength(const uint8_t *msg, size_t len);
void HandleI2NPMessage(uint8_t *msg, size_t len);
void HandleI2NPMessage(std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler {
public:
~I2NPMessagesHandler();
void PutNextMessage(std::shared_ptr<I2NPMessage> &&msg);
void Flush();
private:
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
};
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
void SetMaxNumTransitTunnels(uint16_t maxNumTransitTunnels);
uint16_t GetMaxNumTransitTunnels();
} }
#endif #endif

View file

@ -14,39 +14,35 @@
#include "LittleBigEndian.h" #include "LittleBigEndian.h"
#ifdef NEEDS_LOCAL_ENDIAN #ifdef NEEDS_LOCAL_ENDIAN
uint16_t htobe16(uint16_t int16)
{ uint16_t htobe16(uint16_t int16) {
BigEndian<uint16_t> u16(int16); BigEndian<uint16_t> u16(int16);
return u16.raw_value; return u16.raw_value;
} }
uint32_t htobe32(uint32_t int32) uint32_t htobe32(uint32_t int32) {
{ BigEndian<uint32_t> u32(int32);
BigEndian<uint32_t> u32(int32); return u32.raw_value;
return u32.raw_value;
} }
uint64_t htobe64(uint64_t int64) uint64_t htobe64(uint64_t int64) {
{ BigEndian<uint64_t> u64(int64);
BigEndian<uint64_t> u64(int64); return u64.raw_value;
return u64.raw_value;
} }
uint16_t be16toh(uint16_t big16) uint16_t be16toh(uint16_t big16) {
{ LittleEndian<uint16_t> u16(big16);
LittleEndian<uint16_t> u16(big16); return u16.raw_value;
return u16.raw_value;
} }
uint32_t be32toh(uint32_t big32) uint32_t be32toh(uint32_t big32) {
{ LittleEndian<uint32_t> u32(big32);
LittleEndian<uint32_t> u32(big32); return u32.raw_value;
return u32.raw_value;
} }
uint64_t be64toh(uint64_t big64) uint64_t be64toh(uint64_t big64) {
{ LittleEndian<uint64_t> u64(big64);
LittleEndian<uint64_t> u64(big64); return u64.raw_value;
return u64.raw_value;
} }
#endif #endif

View file

@ -8,6 +8,7 @@
#ifndef I2PENDIAN_H__ #ifndef I2PENDIAN_H__
#define I2PENDIAN_H__ #define I2PENDIAN_H__
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
@ -53,13 +54,19 @@
#else #else
#define NEEDS_LOCAL_ENDIAN #define NEEDS_LOCAL_ENDIAN
#include <cstdint> #include <cstdint>
uint16_t htobe16(uint16_t int16); uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32); uint32_t htobe32(uint32_t int32);
uint64_t htobe64(uint64_t int64); uint64_t htobe64(uint64_t int64);
uint16_t be16toh(uint16_t big16); uint16_t be16toh(uint16_t big16);
uint32_t be32toh(uint32_t big32); uint32_t be32toh(uint32_t big32);
uint64_t be64toh(uint64_t big64); uint64_t be64toh(uint64_t big64);
// assume LittleEndine // assume LittleEndine
@ -72,100 +79,82 @@ uint64_t be64toh(uint64_t big64);
#endif #endif
inline uint16_t buf16toh(const void *buf) inline uint16_t buf16toh(const void *buf) {
{ uint16_t b16;
uint16_t b16; memcpy(&b16, buf, sizeof(uint16_t));
memcpy(&b16, buf, sizeof(uint16_t)); return b16;
return b16;
} }
inline uint32_t buf32toh(const void *buf) inline uint32_t buf32toh(const void *buf) {
{ uint32_t b32;
uint32_t b32; memcpy(&b32, buf, sizeof(uint32_t));
memcpy(&b32, buf, sizeof(uint32_t)); return b32;
return b32;
} }
inline uint64_t buf64toh(const void *buf) inline uint64_t buf64toh(const void *buf) {
{ uint64_t b64;
uint64_t b64; memcpy(&b64, buf, sizeof(uint64_t));
memcpy(&b64, buf, sizeof(uint64_t)); return b64;
return b64;
} }
inline uint16_t bufbe16toh(const void *buf) inline uint16_t bufbe16toh(const void *buf) {
{ return be16toh(buf16toh(buf));
return be16toh(buf16toh(buf));
} }
inline uint32_t bufbe32toh(const void *buf) inline uint32_t bufbe32toh(const void *buf) {
{ return be32toh(buf32toh(buf));
return be32toh(buf32toh(buf));
} }
inline uint64_t bufbe64toh(const void *buf) inline uint64_t bufbe64toh(const void *buf) {
{ return be64toh(buf64toh(buf));
return be64toh(buf64toh(buf));
} }
inline void htobuf16(void *buf, uint16_t b16) inline void htobuf16(void *buf, uint16_t b16) {
{ memcpy(buf, &b16, sizeof(uint16_t));
memcpy(buf, &b16, sizeof(uint16_t));
} }
inline void htobuf32(void *buf, uint32_t b32) inline void htobuf32(void *buf, uint32_t b32) {
{ memcpy(buf, &b32, sizeof(uint32_t));
memcpy(buf, &b32, sizeof(uint32_t));
} }
inline void htobuf64(void *buf, uint64_t b64) inline void htobuf64(void *buf, uint64_t b64) {
{ memcpy(buf, &b64, sizeof(uint64_t));
memcpy(buf, &b64, sizeof(uint64_t));
} }
inline void htobe16buf(void *buf, uint16_t big16) inline void htobe16buf(void *buf, uint16_t big16) {
{ htobuf16(buf, htobe16(big16));
htobuf16(buf, htobe16(big16));
} }
inline void htobe32buf(void *buf, uint32_t big32) inline void htobe32buf(void *buf, uint32_t big32) {
{ htobuf32(buf, htobe32(big32));
htobuf32(buf, htobe32(big32));
} }
inline void htobe64buf(void *buf, uint64_t big64) inline void htobe64buf(void *buf, uint64_t big64) {
{ htobuf64(buf, htobe64(big64));
htobuf64(buf, htobe64(big64));
} }
inline void htole16buf(void *buf, uint16_t big16) inline void htole16buf(void *buf, uint16_t big16) {
{ htobuf16(buf, htole16(big16));
htobuf16(buf, htole16(big16));
} }
inline void htole32buf(void *buf, uint32_t big32) inline void htole32buf(void *buf, uint32_t big32) {
{ htobuf32(buf, htole32(big32));
htobuf32(buf, htole32(big32));
} }
inline void htole64buf(void *buf, uint64_t big64) inline void htole64buf(void *buf, uint64_t big64) {
{ htobuf64(buf, htole64(big64));
htobuf64(buf, htole64(big64));
} }
inline uint16_t bufle16toh(const void *buf) inline uint16_t bufle16toh(const void *buf) {
{ return le16toh(buf16toh(buf));
return le16toh(buf16toh(buf));
} }
inline uint32_t bufle32toh(const void *buf) inline uint32_t bufle32toh(const void *buf) {
{ return le32toh(buf32toh(buf));
return le32toh(buf32toh(buf));
} }
inline uint64_t bufle64toh(const void *buf) inline uint64_t bufle64toh(const void *buf) {
{ return le64toh(buf64toh(buf));
return le64toh(buf64toh(buf));
} }
#endif // I2PENDIAN_H__ #endif // I2PENDIAN_H__

File diff suppressed because it is too large Load diff

View file

@ -20,229 +20,288 @@
#include "Signature.h" #include "Signature.h"
#include "CryptoKey.h" #include "CryptoKey.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data typedef Tag<32> IdentHash;
{
typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{
return ident.ToBase64 ().substr (0, 4);
}
struct Keys inline std::string GetIdentHashAbbreviation(const IdentHash &ident) {
{ return ident.ToBase64().substr(0, 4);
uint8_t privateKey[256]; }
uint8_t signingPrivateKey[20];
uint8_t publicKey[256];
uint8_t signingKey[128];
};
const uint8_t CERTIFICATE_TYPE_NULL = 0; struct Keys {
const uint8_t CERTIFICATE_TYPE_HASHCASH = 1; uint8_t privateKey[256];
const uint8_t CERTIFICATE_TYPE_HIDDEN = 2; uint8_t signingPrivateKey[20];
const uint8_t CERTIFICATE_TYPE_SIGNED = 3; uint8_t publicKey[256];
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; uint8_t signingKey[128];
const uint8_t CERTIFICATE_TYPE_KEY = 5; };
struct Identity const uint8_t CERTIFICATE_TYPE_NULL = 0;
{ const uint8_t CERTIFICATE_TYPE_HASHCASH = 1;
uint8_t publicKey[256]; const uint8_t CERTIFICATE_TYPE_HIDDEN = 2;
uint8_t signingKey[128]; const uint8_t CERTIFICATE_TYPE_SIGNED = 3;
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
const uint8_t CERTIFICATE_TYPE_KEY = 5;
Identity () = default; struct Identity {
Identity (const Keys& keys) { *this = keys; }; uint8_t publicKey[256];
Identity& operator=(const Keys& keys); uint8_t signingKey[128];
size_t FromBuffer (const uint8_t * buf, size_t len); uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
IdentHash Hash () const;
};
Keys CreateRandomKeys (); Identity() = default;
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes Identity(const Keys &keys) { *this = keys; };
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; Identity &operator=(const Keys &keys);
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; size_t FromBuffer(const uint8_t *buf, size_t len);
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only
typedef uint16_t SigningKeyType; IdentHash Hash() const;
typedef uint16_t CryptoKeyType; };
const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 Keys CreateRandomKeys();
class IdentityEx
{
public:
IdentityEx (); const size_t DEFAULT_IDENTITY_SIZE = sizeof(Identity); // 387 bytes
IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey,
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
IdentityEx (const uint8_t * buf, size_t len);
IdentityEx (const IdentityEx& other);
IdentityEx (const Identity& standard);
~IdentityEx ();
IdentityEx& operator=(const IdentityEx& other);
IdentityEx& operator=(const Identity& standard);
size_t FromBuffer (const uint8_t * buf, size_t len); const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
size_t ToBuffer (uint8_t * buf, size_t len) const; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
size_t FromBase64(const std::string& s); const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
std::string ToBase64 () const; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; 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
const IdentHash& GetIdentHash () const { return m_IdentHash; }; const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; }; const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
uint8_t * GetEncryptionPublicKeyBuffer () { return m_StandardIdentity.publicKey; }; const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor (const uint8_t * key) const; const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3;
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
size_t GetSigningPublicKeyLen () const; const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
const uint8_t * GetSigningPublicKeyBuffer () const; // returns NULL for P521 const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
size_t GetSigningPrivateKeyLen () const; const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
size_t GetSignatureLen () const; const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
SigningKeyType GetSigningKeyType () const; const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
bool IsRSA () const; // signing key type const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only
CryptoKeyType GetCryptoKeyType () const;
void DropVerifier () const; // to save memory
bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); } typedef uint16_t SigningKeyType;
void RecalculateIdentHash(uint8_t * buff=nullptr); typedef uint16_t CryptoKeyType;
static i2p::crypto::Verifier * CreateVerifier (SigningKeyType keyType); const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521
static std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor (CryptoKeyType keyType, const uint8_t * key); class IdentityEx {
public:
private: IdentityEx();
void CreateVerifier () const; IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey,
void UpdateVerifier (i2p::crypto::Verifier * verifier) const; SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1,
CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
private: IdentityEx(const uint8_t *buf, size_t len);
Identity m_StandardIdentity; IdentityEx(const IdentityEx &other);
IdentHash m_IdentHash;
mutable i2p::crypto::Verifier * m_Verifier = nullptr;
mutable std::mutex m_VerifierMutex;
size_t m_ExtendedLen;
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
};
class PrivateKeys // for eepsites IdentityEx(const Identity &standard);
{
public:
PrivateKeys () = default; ~IdentityEx();
PrivateKeys (const PrivateKeys& other) { *this = other; };
PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& keys);
PrivateKeys& operator=(const PrivateKeys& other);
~PrivateKeys () = default;
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; }; IdentityEx &operator=(const IdentityEx &other);
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
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();
void RecalculateIdentHash(uint8_t * buf=nullptr) { m_Public->RecalculateIdentHash(buf); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
size_t GetFullLen () const; IdentityEx &operator=(const Identity &standard);
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s); size_t FromBuffer(const uint8_t *buf, size_t len);
std::string ToBase64 () const;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const; size_t ToBuffer(uint8_t *buf, size_t len) const;
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key); size_t FromBase64(const std::string &s);
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 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 std::string ToBase64() const;
PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const;
const std::vector<uint8_t>& GetOfflineSignature () const { return m_OfflineSignature; };
private: const Identity &GetStandardIdentity() const { return m_StandardIdentity; };
void CreateSigner () const; const IdentHash &GetIdentHash() const { return m_IdentHash; };
void CreateSigner (SigningKeyType keyType) const;
size_t GetPrivateKeyLen () const;
private: const uint8_t *GetEncryptionPublicKey() const { return m_StandardIdentity.publicKey; };
std::shared_ptr<IdentityEx> m_Public; uint8_t *GetEncryptionPublicKeyBuffer() { return m_StandardIdentity.publicKey; };
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
std::vector<uint8_t> m_OfflineSignature; // non zero length, if applicable
size_t m_TransientSignatureLen = 0;
size_t m_TransientSigningPrivateKeyLen = 0;
};
// kademlia std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor(const uint8_t *key) const;
struct XORMetric
{
union
{
uint8_t metric[32];
uint64_t metric_ll[4];
};
void SetMin () { memset (metric, 0, 32); }; size_t GetFullLen() const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
void SetMax () { memset (metric, 0xFF, 32); };
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
};
IdentHash CreateRoutingKey (const IdentHash& ident); size_t GetSigningPublicKeyLen() const;
XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
// destination for delivery instructions const uint8_t *GetSigningPublicKeyBuffer() const; // returns NULL for P521
class RoutingDestination size_t GetSigningPrivateKeyLen() const;
{
public:
RoutingDestination () {}; size_t GetSignatureLen() const;
virtual ~RoutingDestination () {};
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0; bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const;
virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) const = 0; // encrypt data for
virtual bool IsDestination () const = 0; // for garlic
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; SigningKeyType GetSigningKeyType() const;
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
};
class LocalDestination bool IsRSA() const; // signing key type
{ CryptoKeyType GetCryptoKeyType() const;
public:
virtual ~LocalDestination() {}; void DropVerifier() const; // to save memory
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; bool operator==(const IdentityEx &other) const { return GetIdentHash() == other.GetIdentHash(); }
virtual bool SupportsEncryptionType (CryptoKeyType keyType) const { return GetIdentity ()->GetCryptoKeyType () == keyType; }; // override for LeaseSet
virtual const uint8_t * GetEncryptionPublicKey (CryptoKeyType keyType) const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet void RecalculateIdentHash(uint8_t *buff = nullptr);
};
} static i2p::crypto::Verifier *CreateVerifier(SigningKeyType keyType);
static std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>
CreateEncryptor(CryptoKeyType keyType, const uint8_t *key);
private:
void CreateVerifier() const;
void UpdateVerifier(i2p::crypto::Verifier *verifier) const;
private:
Identity m_StandardIdentity;
IdentHash m_IdentHash;
mutable i2p::crypto::Verifier *m_Verifier = nullptr;
mutable std::mutex m_VerifierMutex;
size_t m_ExtendedLen;
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
};
class PrivateKeys // for eepsites
{
public:
PrivateKeys() = default;
PrivateKeys(const PrivateKeys &other) { *this = other; };
PrivateKeys(const Keys &keys) { *this = keys; };
PrivateKeys &operator=(const Keys &keys);
PrivateKeys &operator=(const PrivateKeys &other);
~PrivateKeys() = default;
std::shared_ptr<const IdentityEx> GetPublic() const { return m_Public; };
const uint8_t *GetPrivateKey() const { return m_PrivateKey; };
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();
void RecalculateIdentHash(uint8_t *buf = nullptr) { m_Public->RecalculateIdentHash(buf); }
void Sign(const uint8_t *buf, int len, uint8_t *signature) const;
size_t GetFullLen() const;
size_t FromBuffer(const uint8_t *buf, size_t len);
size_t ToBuffer(uint8_t *buf, size_t len) const;
size_t FromBase64(const std::string &s);
std::string ToBase64() const;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor(const uint8_t *key) const;
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor>
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
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;
const std::vector<uint8_t> &GetOfflineSignature() const { return m_OfflineSignature; };
private:
void CreateSigner() const;
void CreateSigner(SigningKeyType keyType) const;
size_t GetPrivateKeyLen() const;
private:
std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
std::vector<uint8_t> m_OfflineSignature; // non zero length, if applicable
size_t m_TransientSignatureLen = 0;
size_t m_TransientSigningPrivateKeyLen = 0;
};
// kademlia
struct XORMetric {
union {
uint8_t metric[32];
uint64_t metric_ll[4];
};
void SetMin() { memset(metric, 0, 32); };
void SetMax() { memset(metric, 0xFF, 32); };
bool operator<(const XORMetric &other) const { return memcmp(metric, other.metric, 32) < 0; };
};
IdentHash CreateRoutingKey(const IdentHash &ident);
XORMetric operator^(const IdentHash &key1, const IdentHash &key2);
// destination for delivery instructions
class RoutingDestination {
public:
RoutingDestination() {};
virtual ~RoutingDestination() {};
virtual std::shared_ptr<const IdentityEx> GetIdentity() const = 0;
virtual void Encrypt(const uint8_t *data, uint8_t *encrypted) const = 0; // encrypt data for
virtual bool IsDestination() const = 0; // for garlic
const IdentHash &GetIdentHash() const { return GetIdentity()->GetIdentHash(); };
virtual CryptoKeyType
GetEncryptionType() const { return GetIdentity()->GetCryptoKeyType(); }; // override in LeaseSet2
};
class LocalDestination {
public:
virtual ~LocalDestination() {};
virtual bool Decrypt(const uint8_t *encrypted, uint8_t *data,
CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity() const = 0;
const IdentHash &GetIdentHash() const { return GetIdentity()->GetIdentHash(); };
virtual bool SupportsEncryptionType(CryptoKeyType keyType) const {
return GetIdentity()->GetCryptoKeyType() == keyType;
}; // override for LeaseSet
virtual const uint8_t *GetEncryptionPublicKey(
CryptoKeyType keyType) const { return GetIdentity()->GetEncryptionPublicKey(); }; // override for LeaseSet
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -19,288 +19,360 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Blinding.h" #include "Blinding.h"
namespace i2p namespace i2p {
{
namespace tunnel namespace tunnel {
{ class InboundTunnel;
class InboundTunnel; }
}
namespace data namespace data {
{ const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds
const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds struct Lease {
struct Lease IdentHash tunnelGateway;
{ uint32_t tunnelID;
IdentHash tunnelGateway; uint64_t endDate; // 0 means invalid
uint32_t tunnelID; bool isUpdated; // transient
uint64_t endDate; // 0 means invalid /* return true if this lease expires within t millisecond + fudge factor */
bool isUpdated; // transient bool ExpiresWithin(const uint64_t t, const uint64_t fudge = 1000) const {
/* return true if this lease expires within t millisecond + fudge factor */ auto expire = i2p::util::GetMillisecondsSinceEpoch();
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const { if (fudge) expire += rand() % fudge;
auto expire = i2p::util::GetMillisecondsSinceEpoch (); if (endDate < expire) return true;
if(fudge) expire += rand() % fudge; return (endDate - expire) < t;
if (endDate < expire) return true; }
return (endDate - expire) < t; };
}
};
struct LeaseCmp struct LeaseCmp {
{ bool operator()(std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const {
bool operator() (std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const if (l1->tunnelID != l2->tunnelID)
{ return l1->tunnelID < l2->tunnelID;
if (l1->tunnelID != l2->tunnelID) else
return l1->tunnelID < l2->tunnelID; return l1->tunnelGateway < l2->tunnelGateway;
else };
return l1->tunnelGateway < l2->tunnelGateway; };
};
};
typedef std::function<bool(const Lease & l)> LeaseInspectFunc; typedef std::function<bool(const Lease &l)> LeaseInspectFunc;
const size_t MAX_LS_BUFFER_SIZE = 3072; const size_t MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8 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 MAX_NUM_LEASES = 16;
const uint8_t NETDB_STORE_TYPE_LEASESET = 1; const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
class LeaseSet: public RoutingDestination
{
public:
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true); class LeaseSet : public RoutingDestination {
virtual ~LeaseSet () { delete[] m_EncryptionKey; delete[] m_Buffer; }; public:
virtual void Update (const uint8_t * buf, size_t len, bool verifySignature = true);
virtual bool IsNewer (const uint8_t * buf, size_t len) const;
void PopulateLeases (); // from buffer
const uint8_t * GetBuffer () const { return m_Buffer; }; LeaseSet(const uint8_t *buf, size_t len, bool storeLeases = true);
size_t GetBufferLen () const { return m_BufferLen; };
bool IsValid () const { return m_IsValid; };
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases (bool withThreshold = true) const;
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const;
bool HasExpiredLeases () const;
bool IsExpired () const;
bool IsEmpty () const { return m_Leases.empty (); };
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
bool ExpiresSoon(const uint64_t dlt=1000 * 5, const uint64_t fudge = 0) const ;
bool operator== (const LeaseSet& other) const
{ 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<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
virtual bool IsPublishedEncrypted () const { return false; };
// implements RoutingDestination virtual ~LeaseSet() {
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; }; delete[] m_EncryptionKey;
void Encrypt (const uint8_t * data, uint8_t * encrypted) const; delete[] m_Buffer;
bool IsDestination () const { return true; }; };
protected: virtual void Update(const uint8_t *buf, size_t len, bool verifySignature = true);
void UpdateLeasesBegin (); virtual bool IsNewer(const uint8_t *buf, size_t len) const;
void UpdateLeasesEnd ();
void UpdateLease (const Lease& lease, uint64_t ts);
// called from LeaseSet2 void PopulateLeases(); // from buffer
LeaseSet (bool storeLeases);
void SetBuffer (const uint8_t * buf, size_t len);
void SetBufferLen (size_t len);
void SetIdentity (std::shared_ptr<const IdentityEx> identity) { m_Identity = identity; };
void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; };
void SetIsValid (bool isValid) { m_IsValid = isValid; };
bool IsStoreLeases () const { return m_StoreLeases; };
private: const uint8_t *GetBuffer() const { return m_Buffer; };
void ReadFromBuffer (bool readIdentity = true, bool verifySignature = true); size_t GetBufferLen() const { return m_BufferLen; };
virtual uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; // returns max expiration time
private: bool IsValid() const { return m_IsValid; };
bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases(bool withThreshold = true) const;
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_EncryptionKey;
uint8_t * m_Buffer;
size_t m_BufferLen;
};
/** const std::vector<std::shared_ptr<const Lease> >
* validate lease set buffer signature and extract expiration timestamp GetNonExpiredLeasesExcluding(LeaseInspectFunc exclude, bool withThreshold = true) const;
* @returns true if the leaseset is well formed and signature is valid
*/
bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires);
const uint8_t NETDB_STORE_TYPE_STANDARD_LEASESET2 = 3; bool HasExpiredLeases() const;
const uint8_t NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 = 5;
const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7;
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; bool IsExpired() const;
const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002;
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2: public LeaseSet bool IsEmpty() const { return m_Leases.empty(); };
{
public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); uint64_t GetExpirationTime() const { return m_ExpirationTime; };
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
uint8_t GetStoreType () const { return m_StoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
bool IsPublic () const { return m_IsPublic; };
bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; };
std::shared_ptr<const i2p::crypto::Verifier> 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 bool ExpiresSoon(const uint64_t dlt = 1000 * 5, const uint64_t fudge = 0) const;
void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
private: bool operator==(const LeaseSet &other) const {
return m_BufferLen == other.m_BufferLen && !memcmp(m_Buffer, other.m_Buffer, m_BufferLen);
};
void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true); virtual uint8_t GetStoreType() const { return NETDB_STORE_TYPE_LEASESET; };
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret);
size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len);
size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len);
template<typename Verifier> virtual uint32_t GetPublishedTimestamp() const { return 0; }; // should be set for LeaseSet2 only
bool VerifySignature (Verifier& verifier, const uint8_t * buf, size_t len, size_t signatureOffset); virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier() const { return nullptr; };
uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; virtual bool IsPublishedEncrypted() const { return false; };
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: // implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity() const { return m_Identity; };
uint8_t m_StoreType; void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
uint32_t m_PublishedTimestamp = 0;
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
CryptoKeyType m_EncryptionType;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
};
// also called from Streaming.cpp bool IsDestination() const { return true; };
template<typename Verifier>
std::shared_ptr<i2p::crypto::Verifier> ProcessOfflineSignature (const Verifier& verifier, const uint8_t * buf, size_t len, size_t& offset) protected:
{
if (offset + 6 >= len) return nullptr; void UpdateLeasesBegin();
const uint8_t * signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp void UpdateLeasesEnd();
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) return nullptr;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; void UpdateLease(const Lease &lease, uint64_t ts);
std::shared_ptr<i2p::crypto::Verifier> transientVerifier (i2p::data::IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return nullptr; // called from LeaseSet2
auto keyLen = transientVerifier->GetPublicKeyLen (); LeaseSet(bool storeLeases);
if (offset + keyLen >= len) return nullptr;
transientVerifier->SetPublicKey (buf + offset); offset += keyLen; void SetBuffer(const uint8_t *buf, size_t len);
if (offset + verifier->GetSignatureLen () >= len) return nullptr;
if (!verifier->Verify (signedData, keyLen + 6, buf + offset)) return nullptr; void SetBufferLen(size_t len);
offset += verifier->GetSignatureLen ();
return transientVerifier; void SetIdentity(std::shared_ptr<const IdentityEx> identity) { m_Identity = identity; };
}
void SetExpirationTime(uint64_t t) { m_ExpirationTime = t; };
void SetIsValid(bool isValid) { m_IsValid = isValid; };
bool IsStoreLeases() const { return m_StoreLeases; };
private:
void ReadFromBuffer(bool readIdentity = true, bool verifySignature = true);
virtual uint64_t
ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const; // returns max expiration time
private:
bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t *m_EncryptionKey;
uint8_t *m_Buffer;
size_t m_BufferLen;
};
/**
* validate lease set buffer signature and extract expiration timestamp
* @returns true if the leaseset is well formed and signature is valid
*/
bool LeaseSetBufferValidate(const uint8_t *ptr, size_t sz, uint64_t &expires);
const uint8_t NETDB_STORE_TYPE_STANDARD_LEASESET2 = 3;
const uint8_t NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 = 5;
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_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2 : public LeaseSet {
public:
LeaseSet2(uint8_t storeType, const uint8_t *buf, size_t len, bool storeLeases = true,
CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
LeaseSet2(const uint8_t *buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
const uint8_t *secret = nullptr,
CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
uint8_t GetStoreType() const { return m_StoreType; };
uint32_t GetPublishedTimestamp() const { return m_PublishedTimestamp; };
bool IsPublic() const { return m_IsPublic; };
bool IsPublishedEncrypted() const { return m_IsPublishedEncrypted; };
std::shared_ptr<const i2p::crypto::Verifier> 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) const;
CryptoKeyType GetEncryptionType() const { return m_EncryptionType; };
private:
void ReadFromBuffer(const uint8_t *buf, size_t len, bool readIdentity = true, bool verifySignature = true);
void ReadFromBufferEncrypted(const uint8_t *buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
const uint8_t *secret);
size_t ReadStandardLS2TypeSpecificPart(const uint8_t *buf, size_t len);
size_t ReadMetaLS2TypeSpecificPart(const uint8_t *buf, size_t len);
template<typename Verifier>
bool VerifySignature(Verifier &verifier, const uint8_t *buf, size_t len, size_t signatureOffset);
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;
uint32_t m_PublishedTimestamp = 0;
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
CryptoKeyType m_EncryptionType;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
};
// also called from Streaming.cpp
template<typename Verifier>
std::shared_ptr<i2p::crypto::Verifier>
ProcessOfflineSignature(const Verifier &verifier, const uint8_t *buf, size_t len, size_t &offset) {
if (offset + 6 >= len) return nullptr;
const uint8_t *signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh(buf + offset);
offset += 4; // expires timestamp
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch()) return nullptr;
uint16_t keyType = bufbe16toh(buf + offset);
offset += 2;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier(i2p::data::IdentityEx::CreateVerifier(keyType));
if (!transientVerifier) return nullptr;
auto keyLen = transientVerifier->GetPublicKeyLen();
if (offset + keyLen >= len) return nullptr;
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();
return transientVerifier;
}
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
class LocalLeaseSet class LocalLeaseSet {
{ public:
public:
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels); LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *encryptionPublicKey,
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
virtual ~LocalLeaseSet () { delete[] m_Buffer; };
virtual uint8_t * GetBuffer () const { return m_Buffer; }; LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *buf, size_t len);
uint8_t * GetSignature () { return GetBuffer () + GetBufferLen () - GetSignatureLen (); };
virtual size_t GetBufferLen () const { return m_BufferLen; };
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
uint8_t * GetLeases () { return m_Leases; };
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; virtual ~LocalLeaseSet() { delete[] m_Buffer; };
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
bool IsExpired () const;
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
bool operator== (const LeaseSet& other) const
{ return GetBufferLen () == other.GetBufferLen () && !memcmp (GetBuffer (), other.GetBuffer (), GetBufferLen ()); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; virtual uint8_t *GetBuffer() const { return m_Buffer; };
virtual const IdentHash& GetStoreHash () const { return GetIdentHash (); }; // differ from ident hash for encrypted LeaseSet2
virtual std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return nullptr; }; // non-null for encrypted LeaseSet2
private: uint8_t *GetSignature() { return GetBuffer() + GetBufferLen() - GetSignatureLen(); };
uint64_t m_ExpirationTime; // in milliseconds virtual size_t GetBufferLen() const { return m_BufferLen; };
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_Buffer, * m_Leases;
size_t m_BufferLen;
};
class LocalLeaseSet2: public LocalLeaseSet size_t GetSignatureLen() const { return m_Identity->GetSignatureLen(); };
{
public:
struct KeySection uint8_t *GetLeases() { return m_Leases; };
{
uint16_t keyType, keyLen;
const uint8_t * encryptionPublicKey;
};
typedef std::vector<KeySection> KeySections;
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, const IdentHash &GetIdentHash() const { return m_Identity->GetIdentHash(); };
const KeySections& encryptionKeys,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP std::shared_ptr<const IdentityEx> GetIdentity() const { return m_Identity; };
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; bool IsExpired() const;
uint8_t * GetBuffer () const { return m_Buffer + 1; }; uint64_t GetExpirationTime() const { return m_ExpirationTime; };
size_t GetBufferLen () const { return m_BufferLen; };
uint8_t GetStoreType () const { return m_Buffer[0]; }; void SetExpirationTime(uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
protected: bool operator==(const LeaseSet &other) const {
return GetBufferLen() == other.GetBufferLen() && !memcmp(GetBuffer(), other.GetBuffer(),
GetBufferLen());
};
LocalLeaseSet2 (std::shared_ptr<const IdentityEx> identity): LocalLeaseSet (identity, nullptr, 0), m_Buffer (nullptr), m_BufferLen(0) {}; // called from LocalEncryptedLeaseSet2 virtual uint8_t GetStoreType() const { return NETDB_STORE_TYPE_LEASESET; };
protected: virtual const IdentHash &
GetStoreHash() const { return GetIdentHash(); }; // differ from ident hash for encrypted LeaseSet2
virtual std::shared_ptr<const LocalLeaseSet>
GetInnerLeaseSet() const { return nullptr; }; // non-null for encrypted LeaseSet2
uint8_t * m_Buffer; // 1 byte store type + actual buffer private:
size_t m_BufferLen;
}; uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t *m_Buffer, *m_Leases;
size_t m_BufferLen;
};
class LocalLeaseSet2 : public LocalLeaseSet {
public:
struct KeySection {
uint16_t keyType, keyLen;
const uint8_t *encryptionPublicKey;
};
typedef std::vector<KeySection> KeySections;
LocalLeaseSet2(uint8_t storeType, const i2p::data::PrivateKeys &keys,
const KeySections &encryptionKeys,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2(uint8_t storeType, std::shared_ptr<const IdentityEx> 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; };
uint8_t GetStoreType() const { return m_Buffer[0]; };
protected:
LocalLeaseSet2(std::shared_ptr<const IdentityEx> identity) : LocalLeaseSet(identity, nullptr, 0),
m_Buffer(nullptr), m_BufferLen(
0) {}; // called from LocalEncryptedLeaseSet2
protected:
uint8_t *m_Buffer; // 1 byte store type + actual buffer
size_t m_BufferLen;
};
const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0; const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0;
const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1; const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1;
const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2; const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2;
typedef i2p::data::Tag<32> AuthPublicKey; typedef i2p::data::Tag<32> AuthPublicKey;
class LocalEncryptedLeaseSet2: public LocalLeaseSet2 class LocalEncryptedLeaseSet2 : public LocalLeaseSet2 {
{ public:
public:
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr); LocalEncryptedLeaseSet2(std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys &keys,
int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE,
std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP LocalEncryptedLeaseSet2(std::shared_ptr<const IdentityEx> identity, const uint8_t *buf,
size_t len); // from I2CP
const IdentHash& GetStoreHash () const { return m_StoreHash; }; const IdentHash &GetStoreHash() const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
private: std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet() const { return m_InnerLeaseSet; };
void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const; private:
private: void CreateClientAuthData(const uint8_t *subcredential, int authType,
std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t *authCookie,
uint8_t *authData) const;
IdentHash m_StoreHash; private:
std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet;
}; IdentHash m_StoreHash;
} std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet;
};
}
} }
#endif #endif

View file

@ -33,218 +33,186 @@ struct BigEndian;
// Little-Endian template // Little-Endian template
#pragma pack(push,1) #pragma pack(push, 1)
template<typename T> template<typename T>
struct LittleEndian struct LittleEndian {
{ union {
union unsigned char bytes[sizeof(T)];
{ T raw_value;
unsigned char bytes[sizeof(T)]; };
T raw_value;
};
LittleEndian(T t = T()) LittleEndian(T t = T()) {
{ operator=(t);
operator =(t); }
}
LittleEndian(const LittleEndian<T> & t) LittleEndian(const LittleEndian<T> &t) {
{ raw_value = t.raw_value;
raw_value = t.raw_value; }
}
LittleEndian(const BigEndian<T> & t) LittleEndian(const BigEndian<T> &t) {
{ for (unsigned i = 0; i < sizeof(T); i++)
for (unsigned i = 0; i < sizeof(T); i++) bytes[i] = t.bytes[sizeof(T) - 1 - i];
bytes[i] = t.bytes[sizeof(T)-1-i]; }
}
operator const T() const operator const T() const {
{ T t = T();
T t = T(); for (unsigned i = 0; i < sizeof(T); i++)
for (unsigned i = 0; i < sizeof(T); i++) t |= T(bytes[i]) << (i << 3);
t |= T(bytes[i]) << (i << 3); return t;
return t; }
}
const T operator = (const T t) const T operator=(const T t) {
{ for (unsigned i = 0; i < sizeof(T); i++)
for (unsigned i = 0; i < sizeof(T); i++) bytes[sizeof(T) - 1 - i] = static_cast<unsigned char>(t >> (i << 3));
bytes[sizeof(T)-1 - i] = static_cast<unsigned char>(t >> (i << 3)); return t;
return t; }
}
// operators // operators
const T operator += (const T t) const T operator+=(const T t) {
{ return (*this = *this + t);
return (*this = *this + t); }
}
const T operator -= (const T t) const T operator-=(const T t) {
{ return (*this = *this - t);
return (*this = *this - t); }
}
const T operator *= (const T t) const T operator*=(const T t) {
{ return (*this = *this * t);
return (*this = *this * t); }
}
const T operator /= (const T t) const T operator/=(const T t) {
{ return (*this = *this / t);
return (*this = *this / t); }
}
const T operator %= (const T t) const T operator%=(const T t) {
{ return (*this = *this % t);
return (*this = *this % t); }
}
LittleEndian<T> operator ++ (int) LittleEndian<T> operator++(int) {
{ LittleEndian<T> tmp(*this);
LittleEndian<T> tmp(*this); operator++();
operator ++ (); return tmp;
return tmp; }
}
LittleEndian<T> & operator ++ () LittleEndian<T> &operator++() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++) ++bytes[i];
{ if (bytes[i] != 0)
++bytes[i]; break;
if (bytes[i] != 0) }
break; return (*this);
} }
return (*this);
}
LittleEndian<T> operator -- (int) LittleEndian<T> operator--(int) {
{ LittleEndian<T> tmp(*this);
LittleEndian<T> tmp(*this); operator--();
operator -- (); return tmp;
return tmp; }
}
LittleEndian<T> & operator -- () LittleEndian<T> &operator--() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++) --bytes[i];
{ if (bytes[i] != (T) (-1))
--bytes[i]; break;
if (bytes[i] != (T)(-1)) }
break; return (*this);
} }
return (*this);
}
}; };
#pragma pack(pop) #pragma pack(pop)
// Big-Endian template // Big-Endian template
#pragma pack(push,1) #pragma pack(push, 1)
template<typename T> template<typename T>
struct BigEndian struct BigEndian {
{ union {
union unsigned char bytes[sizeof(T)];
{ T raw_value;
unsigned char bytes[sizeof(T)]; };
T raw_value;
};
BigEndian(T t = T()) BigEndian(T t = T()) {
{ operator=(t);
operator =(t); }
}
BigEndian(const BigEndian<T> & t) BigEndian(const BigEndian<T> &t) {
{ raw_value = t.raw_value;
raw_value = t.raw_value; }
}
BigEndian(const LittleEndian<T> & t) BigEndian(const LittleEndian<T> &t) {
{ for (unsigned i = 0; i < sizeof(T); i++)
for (unsigned i = 0; i < sizeof(T); i++) bytes[i] = t.bytes[sizeof(T) - 1 - i];
bytes[i] = t.bytes[sizeof(T)-1-i]; }
}
operator const T() const operator const T() const {
{ T t = T();
T t = T(); for (unsigned i = 0; i < sizeof(T); i++)
for (unsigned i = 0; i < sizeof(T); i++) t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3);
t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); return t;
return t; }
}
const T operator = (const T t) const T operator=(const T t) {
{ for (unsigned i = 0; i < sizeof(T); i++)
for (unsigned i = 0; i < sizeof(T); i++) bytes[sizeof(T) - 1 - i] = t >> (i << 3);
bytes[sizeof(T) - 1 - i] = t >> (i << 3); return t;
return t; }
}
// operators // operators
const T operator += (const T t) const T operator+=(const T t) {
{ return (*this = *this + t);
return (*this = *this + t); }
}
const T operator -= (const T t) const T operator-=(const T t) {
{ return (*this = *this - t);
return (*this = *this - t); }
}
const T operator *= (const T t) const T operator*=(const T t) {
{ return (*this = *this * t);
return (*this = *this * t); }
}
const T operator /= (const T t) const T operator/=(const T t) {
{ return (*this = *this / t);
return (*this = *this / t); }
}
const T operator %= (const T t) const T operator%=(const T t) {
{ return (*this = *this % t);
return (*this = *this % t); }
}
BigEndian<T> operator ++ (int) BigEndian<T> operator++(int) {
{ BigEndian<T> tmp(*this);
BigEndian<T> tmp(*this); operator++();
operator ++ (); return tmp;
return tmp; }
}
BigEndian<T> & operator ++ () BigEndian<T> &operator++() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++) ++bytes[sizeof(T) - 1 - i];
{ if (bytes[sizeof(T) - 1 - i] != 0)
++bytes[sizeof(T) - 1 - i]; break;
if (bytes[sizeof(T) - 1 - i] != 0) }
break; return (*this);
} }
return (*this);
}
BigEndian<T> operator -- (int) BigEndian<T> operator--(int) {
{ BigEndian<T> tmp(*this);
BigEndian<T> tmp(*this); operator--();
operator -- (); return tmp;
return tmp; }
}
BigEndian<T> & operator -- () BigEndian<T> &operator--() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++) --bytes[sizeof(T) - 1 - i];
{ if (bytes[sizeof(T) - 1 - i] != (T) (-1))
--bytes[sizeof(T) - 1 - i]; break;
if (bytes[sizeof(T) - 1 - i] != (T)(-1)) }
break; return (*this);
} }
return (*this);
}
}; };
#pragma pack(pop) #pragma pack(pop)
#endif // LITTLEBIGENDIAN_H #endif // LITTLEBIGENDIAN_H

View file

@ -13,237 +13,243 @@
#include <algorithm> #include <algorithm>
namespace i2p { namespace i2p {
namespace log { namespace log {
static Log logger; static Log logger;
/** /**
* @brief Maps our loglevel to their symbolic name * @brief Maps our loglevel to their symbolic name
*/ */
static const char * g_LogLevelStr[eNumLogLevels] = static const char *g_LogLevelStr[eNumLogLevels] =
{ {
"none", // eLogNone "none", // eLogNone
"error", // eLogError "error", // eLogError
"warn", // eLogWarn "warn", // eLogWarn
"info", // eLogInfo "info", // eLogInfo
"debug" // eLogDebug "debug" // eLogDebug
}; };
/** /**
* @brief Colorize log output -- array of terminal control sequences * @brief Colorize log output -- array of terminal control sequences
* @note Using ISO 6429 (ANSI) color sequences * @note Using ISO 6429 (ANSI) color sequences
*/ */
#ifdef _WIN32 #ifdef _WIN32
static const char *LogMsgColors[] = { "", "", "", "", "", "" }; static const char *LogMsgColors[] = { "", "", "", "", "", "" };
#else /* UNIX */ #else /* UNIX */
static const char *LogMsgColors[] = { static const char *LogMsgColors[] = {
[eLogNone] = "\033[0m", /* reset */ [eLogNone] = "\033[0m", /* reset */
[eLogError] = "\033[1;31m", /* red */ [eLogError] = "\033[1;31m", /* red */
[eLogWarning] = "\033[1;33m", /* yellow */ [eLogWarning] = "\033[1;33m", /* yellow */
[eLogInfo] = "\033[1;36m", /* cyan */ [eLogInfo] = "\033[1;36m", /* cyan */
[eLogDebug] = "\033[1;34m", /* blue */ [eLogDebug] = "\033[1;34m", /* blue */
[eNumLogLevels] = "\033[0m", /* reset */ [eNumLogLevels] = "\033[0m", /* reset */
}; };
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
/**
* @brief Maps our log levels to syslog one /**
* @return syslog priority LOG_*, as defined in syslog.h * @brief Maps our log levels to syslog one
*/ * @return syslog priority LOG_*, as defined in syslog.h
static inline int GetSyslogPrio (enum LogLevel l) { */
int priority = LOG_DEBUG; static inline int GetSyslogPrio(enum LogLevel l) {
switch (l) { int priority = LOG_DEBUG;
case eLogNone : priority = LOG_CRIT; break; switch (l) {
case eLogError : priority = LOG_ERR; break; case eLogNone :
case eLogWarning : priority = LOG_WARNING; break; priority = LOG_CRIT;
case eLogInfo : priority = LOG_INFO; break; break;
case eLogDebug : priority = LOG_DEBUG; break; case eLogError :
default : priority = LOG_DEBUG; break; priority = LOG_ERR;
} break;
return priority; case eLogWarning :
} priority = LOG_WARNING;
break;
case eLogInfo :
priority = LOG_INFO;
break;
case eLogDebug :
priority = LOG_DEBUG;
break;
default :
priority = LOG_DEBUG;
break;
}
return priority;
}
#endif #endif
Log::Log(): Log::Log() :
m_Destination(eLogStdout), m_MinLevel(eLogInfo), m_Destination(eLogStdout), m_MinLevel(eLogInfo),
m_LogStream (nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"), m_LogStream(nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
m_IsRunning (false), m_Thread (nullptr) m_IsRunning(false), m_Thread(nullptr) {
{ }
}
Log::~Log () Log::~Log() {
{ delete m_Thread;
delete m_Thread; }
}
void Log::Start () void Log::Start() {
{ if (!m_IsRunning) {
if (!m_IsRunning) m_IsRunning = true;
{ m_Thread = new std::thread(std::bind(&Log::Run, this));
m_IsRunning = true; }
m_Thread = new std::thread (std::bind (&Log::Run, this)); }
}
}
void Log::Stop () void Log::Stop() {
{ switch (m_Destination) {
switch (m_Destination)
{
#ifndef _WIN32 #ifndef _WIN32
case eLogSyslog : case eLogSyslog :
closelog(); closelog();
break; break;
#endif #endif
case eLogFile: case eLogFile:
case eLogStream: case eLogStream:
if (m_LogStream) m_LogStream->flush(); if (m_LogStream) m_LogStream->flush();
break; break;
default: default:
/* do nothing */ /* do nothing */
break; break;
} }
m_IsRunning = false; m_IsRunning = false;
m_Queue.WakeUp (); m_Queue.WakeUp();
if (m_Thread) if (m_Thread) {
{ m_Thread->join();
m_Thread->join (); delete m_Thread;
delete m_Thread; m_Thread = nullptr;
m_Thread = nullptr; }
} }
}
std::string str_tolower(std::string s) { std::string str_tolower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), std::transform(s.begin(), s.end(), s.begin(),
// static_cast<int(*)(int)>(std::tolower) // wrong // static_cast<int(*)(int)>(std::tolower) // wrong
// [](int c){ return std::tolower(c); } // wrong // [](int c){ return std::tolower(c); } // wrong
// [](char c){ return std::tolower(c); } // wrong // [](char c){ return std::tolower(c); } // wrong
[](unsigned char c){ return std::tolower(c); } // correct [](unsigned char c) { return std::tolower(c); } // correct
); );
return s; return s;
} }
void Log::SetLogLevel (const std::string& level_) { void Log::SetLogLevel(const std::string &level_) {
std::string level=str_tolower(level_); std::string level = str_tolower(level_);
if (level == "none") { m_MinLevel = eLogNone; } if (level == "none") { m_MinLevel = eLogNone; }
else if (level == "error") { m_MinLevel = eLogError; } else if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; } else if (level == "warn") { m_MinLevel = eLogWarning; }
else if (level == "info") { m_MinLevel = eLogInfo; } else if (level == "info") { m_MinLevel = eLogInfo; }
else if (level == "debug") { m_MinLevel = eLogDebug; } else if (level == "debug") { m_MinLevel = eLogDebug; }
else { else {
LogPrint(eLogError, "Log: Unknown loglevel: ", level); LogPrint(eLogError, "Log: Unknown loglevel: ", level);
return; return;
} }
LogPrint(eLogInfo, "Log: Logging level set to ", level); LogPrint(eLogInfo, "Log: Logging level set to ", level);
} }
const char * Log::TimeAsString(std::time_t t) { const char *Log::TimeAsString(std::time_t t) {
if (t != m_LastTimestamp) { if (t != m_LastTimestamp) {
strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t)); strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t));
m_LastTimestamp = t; m_LastTimestamp = t;
} }
return m_LastDateTime; return m_LastDateTime;
} }
/** /**
* @note This function better to be run in separate thread due to disk i/o. * @note This function better to be run in separate thread due to disk i/o.
* Unfortunately, with current startup process with late fork() this * Unfortunately, with current startup process with late fork() this
* will give us nothing but pain. Maybe later. See in NetDb as example. * will give us nothing but pain. Maybe later. See in NetDb as example.
*/ */
void Log::Process(std::shared_ptr<LogMsg> msg) void Log::Process(std::shared_ptr <LogMsg> msg) {
{ if (!msg) return;
if (!msg) return; std::hash <std::thread::id> hasher;
std::hash<std::thread::id> hasher; unsigned short short_tid;
unsigned short short_tid; short_tid = (short) (hasher(msg->tid) % 1000);
short_tid = (short) (hasher(msg->tid) % 1000); switch (m_Destination) {
switch (m_Destination) {
#ifndef _WIN32 #ifndef _WIN32
case eLogSyslog: case eLogSyslog:
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str()); syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
break; break;
#endif #endif
case eLogFile: case eLogFile:
case eLogStream: case eLogStream:
if (m_LogStream) if (m_LogStream)
*m_LogStream << TimeAsString(msg->timestamp) *m_LogStream << TimeAsString(msg->timestamp)
<< "@" << short_tid << "@" << short_tid
<< "/" << g_LogLevelStr[msg->level] << "/" << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl; << " - " << msg->text << std::endl;
break; break;
case eLogStdout: case eLogStdout:
default: default:
std::cout << TimeAsString(msg->timestamp) std::cout << TimeAsString(msg->timestamp)
<< "@" << short_tid << "@" << short_tid
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels] << "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl; << LogMsgColors[eNumLogLevels]
break; << " - " << msg->text << std::endl;
} // switch break;
} } // switch
}
void Log::Run () void Log::Run() {
{ i2p::util::SetThreadName("Logging");
i2p::util::SetThreadName("Logging");
Reopen (); Reopen();
while (m_IsRunning) while (m_IsRunning) {
{ std::shared_ptr <LogMsg> msg;
std::shared_ptr<LogMsg> msg; while ((msg = m_Queue.Get()))
while ((msg = m_Queue.Get ())) Process(msg);
Process (msg); if (m_LogStream) m_LogStream->flush();
if (m_LogStream) m_LogStream->flush(); if (m_IsRunning)
if (m_IsRunning) m_Queue.Wait();
m_Queue.Wait (); }
} }
}
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg) void Log::Append(std::shared_ptr <i2p::log::LogMsg> &msg) {
{ m_Queue.Put(msg);
m_Queue.Put(msg); }
}
void Log::SendTo (const std::string& path) void Log::SendTo(const std::string &path) {
{ if (m_LogStream) m_LogStream = nullptr; // close previous
if (m_LogStream) m_LogStream = nullptr; // close previous auto flags = std::ofstream::out | std::ofstream::app;
auto flags = std::ofstream::out | std::ofstream::app; auto os = std::make_shared<std::ofstream>(path, flags);
auto os = std::make_shared<std::ofstream> (path, flags); if (os->is_open()) {
if (os->is_open ()) m_HasColors = false;
{ m_Logfile = path;
m_HasColors = false; m_Destination = eLogFile;
m_Logfile = path; m_LogStream = os;
m_Destination = eLogFile; return;
m_LogStream = os; }
return; LogPrint(eLogError, "Log: Can't open file ", path);
} }
LogPrint(eLogError, "Log: Can't open file ", path);
}
void Log::SendTo (std::shared_ptr<std::ostream> os) { void Log::SendTo(std::shared_ptr <std::ostream> os) {
m_HasColors = false; m_HasColors = false;
m_Destination = eLogStream; m_Destination = eLogStream;
m_LogStream = os; m_LogStream = os;
} }
#ifndef _WIN32 #ifndef _WIN32
void Log::SendTo(const char *name, int facility) {
if (m_MinLevel == eLogNone) return; void Log::SendTo(const char *name, int facility) {
m_HasColors = false; if (m_MinLevel == eLogNone) return;
m_Destination = eLogSyslog; m_HasColors = false;
m_LogStream = nullptr; m_Destination = eLogSyslog;
openlog(name, LOG_CONS | LOG_PID, facility); m_LogStream = nullptr;
} openlog(name, LOG_CONS | LOG_PID, facility);
}
#endif #endif
void Log::Reopen() { void Log::Reopen() {
if (m_Destination == eLogFile) if (m_Destination == eLogFile)
SendTo(m_Logfile); SendTo(m_Logfile);
} }
Log & Logger() { Log &Logger() {
return logger; return logger;
} }
static ThrowFunction g_ThrowFunction; static ThrowFunction g_ThrowFunction;
ThrowFunction GetThrowFunction () { return g_ThrowFunction; }
void SetThrowFunction (ThrowFunction f) { g_ThrowFunction = f; }
} // log ThrowFunction GetThrowFunction() { return g_ThrowFunction; }
void SetThrowFunction(ThrowFunction f) { g_ThrowFunction = f; }
} // log
} // i2p } // i2p

View file

@ -21,158 +21,176 @@
#include "Queue.h" #include "Queue.h"
#ifndef _WIN32 #ifndef _WIN32
#include <syslog.h> #include <syslog.h>
#endif #endif
enum LogLevel enum LogLevel {
{ eLogNone = 0,
eLogNone = 0, eLogError,
eLogError, eLogWarning,
eLogWarning, eLogInfo,
eLogInfo, eLogDebug,
eLogDebug, eNumLogLevels
eNumLogLevels
}; };
enum LogType { enum LogType {
eLogStdout = 0, eLogStdout = 0,
eLogStream, eLogStream,
eLogFile, eLogFile,
#ifndef _WIN32 #ifndef _WIN32
eLogSyslog, eLogSyslog,
#endif #endif
}; };
namespace i2p { namespace i2p {
namespace log { namespace log {
struct LogMsg; /* forward declaration */ struct LogMsg; /* forward declaration */
class Log class Log {
{ private:
private:
enum LogType m_Destination; enum LogType m_Destination;
enum LogLevel m_MinLevel; enum LogLevel m_MinLevel;
std::shared_ptr<std::ostream> m_LogStream; std::shared_ptr <std::ostream> m_LogStream;
std::string m_Logfile; std::string m_Logfile;
std::time_t m_LastTimestamp; std::time_t m_LastTimestamp;
char m_LastDateTime[64]; char m_LastDateTime[64];
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue; i2p::util::Queue<std::shared_ptr < LogMsg> >
bool m_HasColors; m_Queue;
std::string m_TimeFormat; bool m_HasColors;
volatile bool m_IsRunning; std::string m_TimeFormat;
std::thread * m_Thread; volatile bool m_IsRunning;
std::thread *m_Thread;
private: private:
/** prevent making copies */ /** prevent making copies */
Log (const Log &); Log(const Log &);
const Log& operator=(const Log&);
void Run (); const Log &operator=(const Log &);
void Process (std::shared_ptr<LogMsg> msg);
/** void Run();
* @brief Makes formatted string from unix timestamp
* @param ts Second since epoch
*
* This function internally caches the result for last provided value
*/
const char * TimeAsString(std::time_t ts);
public: void Process(std::shared_ptr <LogMsg> msg);
Log (); /**
~Log (); * @brief Makes formatted string from unix timestamp
* @param ts Second since epoch
*
* This function internally caches the result for last provided value
*/
const char *TimeAsString(std::time_t ts);
LogType GetLogType () { return m_Destination; }; public:
LogLevel GetLogLevel () { return m_MinLevel; };
void Start (); Log();
void Stop ();
/** ~Log();
* @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level
*/
void SetLogLevel (const std::string& level);
/** LogType GetLogType() { return m_Destination; };
* @brief Sets log destination to logfile
* @param path Path to logfile
*/
void SendTo (const std::string &path);
/** LogLevel GetLogLevel() { return m_MinLevel; };
* @brief Sets log destination to given output stream
* @param os Output stream
*/
void SendTo (std::shared_ptr<std::ostream> os);
/** void Start();
* @brief Sets format for timestamps in log
* @param format String with timestamp format
*/
void SetTimeFormat (std::string format) { m_TimeFormat = format; };
#ifndef _WIN32 void Stop();
/**
* @brief Sets log destination to syslog
* @param name Wanted program name
* @param facility Wanted log category
*/
void SendTo (const char *name, int facility);
#endif
/** /**
* @brief Format log message and write to output stream/syslog * @brief Sets minimal allowed level for log messages
* @param msg Pointer to processed message * @param level String with wanted minimal msg level
*/ */
void Append(std::shared_ptr<i2p::log::LogMsg> &); void SetLogLevel(const std::string &level);
/** @brief Reopen log file */ /**
void Reopen(); * @brief Sets log destination to logfile
}; * @param path Path to logfile
*/
void SendTo(const std::string &path);
/** /**
* @struct LogMsg * @brief Sets log destination to given output stream
* @brief Log message container * @param os Output stream
* */
* We creating it somewhere with LogPrint(), void SendTo(std::shared_ptr <std::ostream> os);
* then put in MsgQueue for later processing.
*/
struct LogMsg {
std::time_t timestamp;
std::string text; /**< message text as single string */
LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, std::string&& txt): timestamp(ts), text(std::move(txt)), level(lvl) {} /**
}; * @brief Sets format for timestamps in log
* @param format String with timestamp format
*/
void SetTimeFormat(std::string format) { m_TimeFormat = format; };
Log & Logger(); #ifndef _WIN32
typedef std::function<void (const std::string&)> ThrowFunction; /**
ThrowFunction GetThrowFunction (); * @brief Sets log destination to syslog
void SetThrowFunction (ThrowFunction f); * @param name Wanted program name
} // log * @param facility Wanted log category
*/
void SendTo(const char *name, int facility);
#endif
/**
* @brief Format log message and write to output stream/syslog
* @param msg Pointer to processed message
*/
void Append(std::shared_ptr <i2p::log::LogMsg> &);
/** @brief Reopen log file */
void Reopen();
};
/**
* @struct LogMsg
* @brief Log message container
*
* We creating it somewhere with LogPrint(),
* then put in MsgQueue for later processing.
*/
struct LogMsg {
std::time_t timestamp;
std::string text; /**< message text as single string */
LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */
LogMsg(LogLevel lvl, std::time_t ts, std::string &&txt) : timestamp(ts), text(std::move(txt)), level(lvl) {}
};
Log &Logger();
typedef std::function<void(const std::string &)> ThrowFunction;
ThrowFunction GetThrowFunction();
void SetThrowFunction(ThrowFunction f);
} // log
} // i2p } // i2p
/** internal usage only -- folding args array to single string */ /** internal usage only -- folding args array to single string */
template<typename TValue> template<typename TValue>
void LogPrint (std::stringstream& s, TValue&& arg) noexcept void LogPrint(std::stringstream &s, TValue &&arg)
noexcept
{ {
s << std::forward<TValue>(arg); s <<
std::forward<TValue>(arg);
} }
#if (__cplusplus < 201703L) // below C++ 17 #if (__cplusplus < 201703L) // below C++ 17
/** internal usage only -- folding args array to single string */ /** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs> template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept void LogPrint(std::stringstream &s, TValue &&arg, TArgs &&... args)
noexcept
{ {
LogPrint (s, std::forward<TValue>(arg)); LogPrint (s, std::forward<TValue>(arg)
LogPrint (s, std::forward<TArgs>(args)...); );
LogPrint (s, std::forward<TArgs>(args)
...);
} }
#endif #endif
@ -182,24 +200,33 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
* @param args Array of message parts * @param args Array of message parts
*/ */
template<typename... TArgs> template<typename... TArgs>
void LogPrint (LogLevel level, TArgs&&... args) noexcept void LogPrint(LogLevel level, TArgs &&... args)
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
return;
// fold message to single string noexcept
std::stringstream ss; {
i2p::log::Log &log = i2p::log::Logger();
if (level > log.
GetLogLevel()
)
return;
// fold message to single string
std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher #if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else #else
LogPrint (ss, std::forward<TArgs>(args)...); LogPrint (ss, std::forward<TArgs>(args)
...);
#endif #endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str()); auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id(); msg->
log.Append(msg); tid = std::this_thread::get_id();
log.
Append(msg);
} }
/** /**
@ -207,18 +234,26 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
* @param args Array of message parts * @param args Array of message parts
*/ */
template<typename... TArgs> template<typename... TArgs>
void ThrowFatal (TArgs&&... args) noexcept void ThrowFatal(TArgs &&... args)
noexcept
{ {
auto f = i2p::log::GetThrowFunction (); auto f = i2p::log::GetThrowFunction();
if (!f) return; if (!f) return;
// fold message to single string // fold message to single string
std::stringstream ss(""); std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher #if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else #else
LogPrint (ss, std::forward<TArgs>(args)...); LogPrint (ss, std::forward<TArgs>(args)
...);
#endif #endif
f (ss.str ()); f (ss
.
str()
);
} }
#endif // LOG_H__ #endif // LOG_H__

File diff suppressed because it is too large Load diff

View file

@ -22,276 +22,339 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287;
const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287;
const int NTCP2_MAX_PADDING_RATIO = 6; // in % const int NTCP2_MAX_PADDING_RATIO = 6; // in %
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25 * 60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25 * 60; // 25 minuntes
const int NTCP2_CLOCK_SKEW = 60; // in seconds const int NTCP2_CLOCK_SKEW = 60; // in seconds
const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up
enum NTCP2BlockType enum NTCP2BlockType {
{ eNTCP2BlkDateTime = 0,
eNTCP2BlkDateTime = 0, eNTCP2BlkOptions, // 1
eNTCP2BlkOptions, // 1 eNTCP2BlkRouterInfo, // 2
eNTCP2BlkRouterInfo, // 2 eNTCP2BlkI2NPMessage, // 3
eNTCP2BlkI2NPMessage, // 3 eNTCP2BlkTermination, // 4
eNTCP2BlkTermination, // 4 eNTCP2BlkPadding = 254
eNTCP2BlkPadding = 254 };
};
enum NTCP2TerminationReason enum NTCP2TerminationReason {
{ eNTCP2NormalClose = 0,
eNTCP2NormalClose = 0, eNTCP2TerminationReceived, // 1
eNTCP2TerminationReceived, // 1 eNTCP2IdleTimeout, // 2
eNTCP2IdleTimeout, // 2 eNTCP2RouterShutdown, // 3
eNTCP2RouterShutdown, // 3 eNTCP2DataPhaseAEADFailure, // 4
eNTCP2DataPhaseAEADFailure, // 4 eNTCP2IncompatibleOptions, // 5
eNTCP2IncompatibleOptions, // 5 eNTCP2IncompatibleSignatureType, // 6
eNTCP2IncompatibleSignatureType, // 6 eNTCP2ClockSkew, // 7
eNTCP2ClockSkew, // 7 eNTCP2PaddingViolation, // 8
eNTCP2PaddingViolation, // 8 eNTCP2AEADFramingError, // 9
eNTCP2AEADFramingError, // 9 eNTCP2PayloadFormatError, // 10
eNTCP2PayloadFormatError, // 10 eNTCP2Message1Error, // 11
eNTCP2Message1Error, // 11 eNTCP2Message2Error, // 12
eNTCP2Message2Error, // 12 eNTCP2Message3Error, // 13
eNTCP2Message3Error, // 13 eNTCP2IntraFrameReadTimeout, // 14
eNTCP2IntraFrameReadTimeout, // 14 eNTCP2RouterInfoSignatureVerificationFail, // 15
eNTCP2RouterInfoSignatureVerificationFail, // 15 eNTCP2IncorrectSParameter, // 16
eNTCP2IncorrectSParameter, // 16 eNTCP2Banned, // 17
eNTCP2Banned, // 17 };
};
// RouterInfo flags // RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState struct NTCP2Establisher : private i2p::crypto::NoiseSymmetricState {
{ NTCP2Establisher();
NTCP2Establisher ();
~NTCP2Establisher ();
const uint8_t * GetPub () const { return m_EphemeralKeys->GetPublicKey (); }; ~NTCP2Establisher();
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
const uint8_t * GetK () const { return m_CK + 32; }; const uint8_t *GetPub() const { return m_EphemeralKeys->GetPublicKey(); };
const uint8_t * GetCK () const { return m_CK; };
const uint8_t * GetH () const { return m_H; };
void KDF1Alice (); const uint8_t *GetRemotePub() const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
void KDF1Bob (); uint8_t *GetRemotePub() { return m_RemoteEphemeralPublicKey; }; // to set
void KDF2Alice ();
void KDF2Bob ();
void KDF3Alice (); // for SessionConfirmed part 2
void KDF3Bob ();
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH const uint8_t *GetK() const { return m_CK + 32; };
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void CreateEphemeralKey ();
void CreateSessionRequestMessage (); const uint8_t *GetCK() const { return m_CK; };
void CreateSessionCreatedMessage ();
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew); const uint8_t *GetH() const { return m_H; };
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys; void KDF1Alice();
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16];
i2p::data::IdentHash m_RemoteIdentHash;
uint16_t m3p2Len;
uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], void KDF1Bob();
m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer;
size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
}; void KDF2Alice();
class NTCP2Server; void KDF2Bob();
class NTCP2Session: public TransportSession, public std::enable_shared_from_this<NTCP2Session>
{
public:
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr, void KDF3Alice(); // for SessionConfirmed part 2
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr); void KDF3Bob();
~NTCP2Session ();
void Terminate ();
void TerminateByTimeout ();
void Done ();
void Close () { m_Socket.close (); }; // for accept
void DeleteNextReceiveBuffer (uint64_t ts);
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; void KeyDerivationFunction1(const uint8_t *pub, i2p::crypto::X25519Keys &priv, const uint8_t *rs,
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; const uint8_t *epub); // for SessionRequest, (pub, priv) for DH
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; }; void KeyDerivationFunction2(const uint8_t *sessionRequest, size_t sessionRequestLen,
const uint8_t *epub); // for SessionCreate
void CreateEphemeralKey();
bool IsEstablished () const { return m_IsEstablished; }; void CreateSessionRequestMessage();
bool IsTerminated () const { return m_IsTerminated; };
void ClientLogin (); // Alice void CreateSessionCreatedMessage();
void ServerLogin (); // Bob
void SendLocalRouterInfo (bool update); // after handshake or by update void CreateSessionConfirmedMessagePart1(const uint8_t *nonce);
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
private: void CreateSessionConfirmedMessagePart2(const uint8_t *nonce);
void Established (); bool ProcessSessionRequestMessage(uint16_t &paddingLen, bool &clockSkew);
void CreateNonce (uint64_t seqn, uint8_t * nonce); bool ProcessSessionCreatedMessage(uint16_t &paddingLen);
void CreateNextReceivedBuffer (size_t size);
void KeyDerivationFunctionDataPhase ();
void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey);
// establish bool ProcessSessionConfirmedMessagePart1(const uint8_t *nonce);
void SendSessionRequest ();
void SendSessionCreated ();
void SendSessionConfirmed ();
void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); bool ProcessSessionConfirmedMessagePart2(const uint8_t *nonce, uint8_t *m3p2Buf);
void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
// data std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
void ReceiveLength (); uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred); uint8_t m_RemoteStaticKey[32], m_IV[16];
void Receive (); i2p::data::IdentHash m_RemoteIdentHash;
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); uint16_t m3p2Len;
void ProcessNextFrame (const uint8_t * frame, size_t len);
void SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf); uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE],
void SendI2NPMsgs (std::vector<std::shared_ptr<I2NPMessage> >& msgs); m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], *m_SessionConfirmedBuffer;
void HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs); size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
void EncryptAndSendNextBuffer (size_t payloadLen);
void HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
size_t CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len);
void SendQueue ();
void SendRouterInfo ();
void SendTermination (NTCP2TerminationReason reason);
void SendTerminationAndTerminate (NTCP2TerminationReason reason);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
private: };
NTCP2Server& m_Server; class NTCP2Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsEstablished, m_IsTerminated;
std::unique_ptr<NTCP2Establisher> m_Establisher; class NTCP2Session : public TransportSession, public std::enable_shared_from_this<NTCP2Session> {
// data phase public:
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32];
const uint8_t * m_SendKey, * m_ReceiveKey; NTCP2Session(NTCP2Server &server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
~NTCP2Session();
void Terminate();
void TerminateByTimeout();
void Done();
void Close() { m_Socket.close(); }; // for accept
void DeleteNextReceiveBuffer(uint64_t ts);
boost::asio::ip::tcp::socket &GetSocket() { return m_Socket; };
const boost::asio::ip::tcp::endpoint &GetRemoteEndpoint() { return m_RemoteEndpoint; };
void SetRemoteEndpoint(const boost::asio::ip::tcp::endpoint &ep) { m_RemoteEndpoint = ep; };
bool IsEstablished() const { return m_IsEstablished; };
bool IsTerminated() const { return m_IsTerminated; };
void ClientLogin(); // Alice
void ServerLogin(); // Bob
void SendLocalRouterInfo(bool update); // after handshake or by update
void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs);
private:
void Established();
void CreateNonce(uint64_t seqn, uint8_t *nonce);
void CreateNextReceivedBuffer(size_t size);
void KeyDerivationFunctionDataPhase();
void SetSipKeys(const uint8_t *sendSipKey, const uint8_t *receiveSipKey);
// establish
void SendSessionRequest();
void SendSessionCreated();
void SendSessionConfirmed();
void HandleSessionRequestSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionRequestReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void
HandleSessionRequestPaddingReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionCreatedSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionCreatedReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void
HandleSessionCreatedPaddingReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
// data
void ReceiveLength();
void HandleReceivedLength(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void Receive();
void HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void ProcessNextFrame(const uint8_t *frame, size_t len);
void SetNextSentFrameLength(size_t frameLen, uint8_t *lengthBuf);
void SendI2NPMsgs(std::vector<std::shared_ptr<I2NPMessage> > &msgs);
void HandleI2NPMsgsSent(const boost::system::error_code &ecode, std::size_t bytes_transferred,
std::vector<std::shared_ptr<I2NPMessage> > msgs);
void EncryptAndSendNextBuffer(size_t payloadLen);
void HandleNextFrameSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
size_t CreatePaddingBlock(size_t msgLen, uint8_t *buf, size_t len);
void SendQueue();
void SendRouterInfo();
void SendTermination(NTCP2TerminationReason reason);
void SendTerminationAndTerminate(NTCP2TerminationReason reason);
void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
private:
NTCP2Server &m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsEstablished, m_IsTerminated;
std::unique_ptr<NTCP2Establisher> m_Establisher;
// data phase
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32];
const uint8_t *m_SendKey, *m_ReceiveKey;
#if OPENSSL_SIPHASH #if OPENSSL_SIPHASH
EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx;
#else #else
const uint8_t * m_SendSipKey, * m_ReceiveSipKey; const uint8_t *m_SendSipKey, *m_ReceiveSipKey;
#endif #endif
uint16_t m_NextReceivedLen; uint16_t m_NextReceivedLen;
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; uint8_t *m_NextReceivedBuffer, *m_NextSendBuffer;
size_t m_NextReceivedBufferSize; size_t m_NextReceivedBufferSize;
union union {
{ uint8_t buf[8];
uint8_t buf[8]; uint16_t key;
uint16_t key; } m_ReceiveIV, m_SendIV;
} m_ReceiveIV, m_SendIV; uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber;
uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
bool m_IsSending, m_IsReceiving; bool m_IsSending, m_IsReceiving;
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_NextRouterInfoResendTime; // seconds since epoch uint64_t m_NextRouterInfoResendTime; // seconds since epoch
uint16_t m_PaddingSizes[16]; uint16_t m_PaddingSizes[16];
int m_NextPaddingSize; int m_NextPaddingSize;
}; };
class NTCP2Server: private i2p::util::RunnableServiceWithWork class NTCP2Server : private i2p::util::RunnableServiceWithWork {
{ public:
public:
enum ProxyType enum ProxyType {
{ eNoProxy,
eNoProxy, eSocksProxy,
eSocksProxy, eHTTPProxy
eHTTPProxy };
};
NTCP2Server (); NTCP2Server();
~NTCP2Server ();
void Start (); ~NTCP2Server();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false); void Start();
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
void ConnectWithProxy (std::shared_ptr<NTCP2Session> conn); void Stop();
void Connect(std::shared_ptr<NTCP2Session> conn);
bool UsingProxy() const { return m_ProxyType != eNoProxy; }; boost::asio::io_service &GetService() { return GetIOService(); };
void UseProxy(ProxyType proxy, const std::string& address, uint16_t port, const std::string& user, const std::string& pass);
void SetLocalAddress (const boost::asio::ip::address& localAddress); bool AddNTCP2Session(std::shared_ptr<NTCP2Session> session, bool incoming = false);
private: void RemoveNTCP2Session(std::shared_ptr<NTCP2Session> session);
void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error); std::shared_ptr<NTCP2Session> FindNTCP2Session(const i2p::data::IdentHash &ident);
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer); void ConnectWithProxy(std::shared_ptr<NTCP2Session> conn);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer void Connect(std::shared_ptr<NTCP2Session> conn);
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private: bool UsingProxy() const { return m_ProxyType != eNoProxy; };
boost::asio::deadline_timer m_TerminationTimer; void UseProxy(ProxyType proxy, const std::string &address, uint16_t port, const std::string &user,
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor; const std::string &pass);
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType; void SetLocalAddress(const boost::asio::ip::address &localAddress);
std::string m_ProxyAddress, m_ProxyAuthorization;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
public: private:
// for HTTP/I2PControl void HandleAccept(std::shared_ptr<NTCP2Session> conn, const boost::system::error_code &error);
const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; };
}; void HandleAcceptV6(std::shared_ptr<NTCP2Session> conn, const boost::system::error_code &error);
}
void HandleConnect(const boost::system::error_code &ecode, std::shared_ptr<NTCP2Session> conn,
std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code &ecode, std::shared_ptr<NTCP2Session> conn,
std::shared_ptr<boost::asio::deadline_timer> timer);
void
AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
private:
boost::asio::deadline_timer m_TerminationTimer;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType;
std::string m_ProxyAddress, m_ProxyAuthorization;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
public:
// for HTTP/I2PControl
const decltype(m_NTCP2Sessions)
&
GetNTCP2Sessions() const { return m_NTCP2Sessions; };
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -32,162 +32,215 @@
#include "version.h" #include "version.h"
#include "util.h" #include "util.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data const int NETDB_MIN_ROUTERS = 90;
{ const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_MIN_ROUTERS = 90; const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds
const int NETDB_MIN_FLOODFILLS = 5; const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60;
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours const int NETDB_PUBLISH_INTERVAL = 60 * 40;
const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int NETDB_PUBLISH_INTERVAL = 60 * 40; const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36
const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
/** function for visiting a leaseset stored in a floodfill */ /** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor; typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
/** function for visiting a router info we have locally */ /** function for visiting a router info we have locally */
typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor; typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor;
/** function for visiting a router info and determining if we want to use it */ /** function for visiting a router info and determining if we want to use it */
typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter; typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
class NetDb class NetDb {
{ public:
public:
NetDb (); NetDb();
~NetDb ();
void Start (); ~NetDb();
void Stop ();
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len); void Start();
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true); void Stop();
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<const RouterInfo> AddRouterInfo(const uint8_t *buf, int len);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const; bool AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len);
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg); bool AddLeaseSet(const IdentHash &ident, const uint8_t *buf, int len);
/** set hidden mode, aka don't publish our RI to netdb and don't explore */ bool AddLeaseSet2(const IdentHash &ident, const uint8_t *buf, int len, uint8_t storeType);
void SetHidden(bool hide);
void Reseed (); std::shared_ptr<RouterInfo> FindRouter(const IdentHash &ident) const;
Families& GetFamilies () { return m_Families; };
// for web interface std::shared_ptr<LeaseSet> FindLeaseSet(const IdentHash &destination) const;
int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
/** visit all lease sets we currently store */ std::shared_ptr<RouterProfile> FindRouterProfile(const IdentHash &ident) const;
void VisitLeaseSets(LeaseSetVisitor v);
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
void VisitStoredRouterInfos(RouterInfoVisitor v);
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
void VisitRouterInfos(RouterInfoVisitor v);
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); }; void RequestDestination(const IdentHash &destination,
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; RequestedDestination::RequestComplete requestComplete = nullptr,
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r); bool direct = true);
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; }; void RequestDestinationFrom(const IdentHash &destination, const IdentHash &from, bool exploritory,
RequestedDestination::RequestComplete requestComplete = nullptr);
private: void HandleDatabaseStoreMsg(std::shared_ptr<const I2NPMessage> msg);
void Load (); void HandleDatabaseSearchReplyMsg(std::shared_ptr<const I2NPMessage> msg);
bool LoadRouterInfo (const std::string& path, uint64_t ts);
void SaveUpdated ();
void Run (); // exploratory thread
void Explore (int numDestinations);
void Publish ();
void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg);
void ManageLeaseSets ();
void ManageRequests ();
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters = 40, int numFloodfills = 20); void HandleDatabaseLookupMsg(std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len, bool& updated); void HandleNTCP2RouterInfoMsg(std::shared_ptr<const I2NPMessage> m);
std::shared_ptr<const RouterInfo> AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated);
template<typename Filter> void HandleDeliveryStatusMsg(std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
private: std::shared_ptr<const RouterInfo> GetRandomRouter() const;
mutable std::mutex m_LeaseSetsMutex; std::shared_ptr<const RouterInfo>
std::unordered_map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets; GetRandomRouter(std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
mutable std::mutex m_RouterInfosMutex;
std::unordered_map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
mutable std::mutex m_FloodfillsMutex;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
bool m_IsRunning; std::shared_ptr<const RouterInfo>
std::thread * m_Thread; GetHighBandwidthRandomRouter(std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator; std::shared_ptr<const RouterInfo>
Reseeder * m_Reseeder; GetRandomPeerTestRouter(bool v4, const std::set<IdentHash> &excluded) const;
Families m_Families;
i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests; std::shared_ptr<const RouterInfo>
NetDbRequests m_Requests; GetRandomSSU2PeerTestRouter(bool v4, const std::set<IdentHash> &excluded) const;
bool m_PersistProfiles; std::shared_ptr<const RouterInfo> GetRandomSSUV6Router() const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer(bool v4, const std::set<IdentHash> &excluded) const;
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/ std::shared_ptr<const RouterInfo>
std::shared_ptr<RouterInfo> m_FloodfillBootstrap; GetRandomSSU2Introducer(bool v4, const std::set<IdentHash> &excluded) const;
/** true if in hidden mode */ std::shared_ptr<const RouterInfo>
bool m_HiddenMode; GetClosestFloodfill(const IdentHash &destination, const std::set<IdentHash> &excluded,
bool closeThanUsOnly = false) const;
std::set<IdentHash> m_PublishExcluded; std::vector<IdentHash> GetClosestFloodfills(const IdentHash &destination, size_t num,
uint32_t m_PublishReplyToken = 0; std::set<IdentHash> &excluded,
bool closeThanUsOnly = false) const;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool; std::shared_ptr<const RouterInfo>
i2p::util::MemoryPoolMt<Lease> m_LeasesPool; GetClosestNonFloodfill(const IdentHash &destination, const std::set<IdentHash> &excluded) const;
};
extern NetDb netdb; std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(FamilyID fam) const;
}
void SetUnreachable(const IdentHash &ident, bool unreachable);
void PostI2NPMsg(std::shared_ptr<const I2NPMessage> msg);
/** 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; };
// for web interface
int GetNumRouters() const { return m_RouterInfos.size(); };
int GetNumFloodfills() const { return m_Floodfills.size(); };
int GetNumLeaseSets() const { return m_LeaseSets.size(); };
/** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v);
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
void VisitStoredRouterInfos(RouterInfoVisitor v);
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
void VisitRouterInfos(RouterInfoVisitor v);
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos() { m_RouterInfos.clear(); };
std::shared_ptr<RouterInfo::Buffer>
NewRouterInfoBuffer() { return m_RouterInfoBuffersPool.AcquireSharedMt(); };
void PopulateRouterInfoBuffer(std::shared_ptr<RouterInfo> r);
std::shared_ptr<Lease> NewLease(const Lease &lease) { return m_LeasesPool.AcquireSharedMt(lease); };
uint32_t GetPublishReplyToken() const { return m_PublishReplyToken; };
private:
void Load();
bool LoadRouterInfo(const std::string &path, uint64_t ts);
void SaveUpdated();
void Run(); // exploratory thread
void Explore(int numDestinations);
void Publish();
void Flood(const IdentHash &ident, std::shared_ptr<I2NPMessage> floodMsg);
void ManageLeaseSets();
void ManageRequests();
void ReseedFromFloodfill(const RouterInfo &ri, int numRouters = 40, int numFloodfills = 20);
std::shared_ptr<const RouterInfo> AddRouterInfo(const uint8_t *buf, int len, bool &updated);
std::shared_ptr<const RouterInfo>
AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len, bool &updated);
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter(Filter filter) const;
private:
mutable std::mutex m_LeaseSetsMutex;
std::unordered_map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex;
std::unordered_map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
mutable std::mutex m_FloodfillsMutex;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
bool m_IsRunning;
std::thread *m_Thread;
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator;
Reseeder *m_Reseeder;
Families m_Families;
i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests;
NetDbRequests m_Requests;
bool m_PersistProfiles;
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
/** true if in hidden mode */
bool m_HiddenMode;
std::set<IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken = 0;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
};
extern NetDb netdb;
}
} }
#endif #endif

View file

@ -12,157 +12,142 @@
#include "NetDb.hpp" #include "NetDb.hpp"
#include "NetDbRequests.h" #include "NetDbRequests.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data std::shared_ptr<I2NPMessage>
{ RequestedDestination::CreateRequestMessage(std::shared_ptr<const RouterInfo> router,
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) {
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) std::shared_ptr<I2NPMessage> msg;
{ if (replyTunnel)
std::shared_ptr<I2NPMessage> msg; msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
if(replyTunnel) replyTunnel->GetNextIdentHash(),
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, replyTunnel->GetNextTunnelID(), m_IsExploratory,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers);
&m_ExcludedPeers); else
else msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0,
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); m_IsExploratory, &m_ExcludedPeers);
if(router) if (router)
m_ExcludedPeers.insert (router->GetIdentHash ()); m_ExcludedPeers.insert(router->GetIdentHash());
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg; return msg;
} }
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage(const IdentHash &floodfill) {
{ auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, i2p::context.GetRouterInfo().GetIdentHash(), 0, false,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill); m_ExcludedPeers.insert(floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg; return msg;
} }
void RequestedDestination::ClearExcludedPeers () void RequestedDestination::ClearExcludedPeers() {
{ m_ExcludedPeers.clear();
m_ExcludedPeers.clear (); }
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r) void RequestedDestination::Success(std::shared_ptr<RouterInfo> r) {
{ if (m_RequestComplete) {
if (m_RequestComplete) m_RequestComplete(r);
{ m_RequestComplete = nullptr;
m_RequestComplete (r); }
m_RequestComplete = nullptr; }
}
}
void RequestedDestination::Fail () void RequestedDestination::Fail() {
{ if (m_RequestComplete) {
if (m_RequestComplete) m_RequestComplete(nullptr);
{ m_RequestComplete = nullptr;
m_RequestComplete (nullptr); }
m_RequestComplete = nullptr; }
}
}
void NetDbRequests::Start () void NetDbRequests::Start() {
{ }
}
void NetDbRequests::Stop () void NetDbRequests::Stop() {
{ m_RequestedDestinations.clear();
m_RequestedDestinations.clear (); }
}
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete) std::shared_ptr<RequestedDestination>
{ NetDbRequests::CreateRequest(const IdentHash &destination, bool isExploratory,
// request RouterInfo directly RequestedDestination::RequestComplete requestComplete) {
auto dest = std::make_shared<RequestedDestination> (destination, isExploratory); // request RouterInfo directly
dest->SetRequestComplete (requestComplete); auto dest = std::make_shared<RequestedDestination>(destination, isExploratory);
{ dest->SetRequestComplete(requestComplete);
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); {
if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
return nullptr; if (!m_RequestedDestinations.insert(std::make_pair(destination, dest)).second) // not inserted
} return nullptr;
return dest; }
} return dest;
}
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r) void NetDbRequests::RequestComplete(const IdentHash &ident, std::shared_ptr<RouterInfo> r) {
{ std::shared_ptr<RequestedDestination> request;
std::shared_ptr<RequestedDestination> request; {
{ std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); auto it = m_RequestedDestinations.find(ident);
auto it = m_RequestedDestinations.find (ident); if (it != m_RequestedDestinations.end()) {
if (it != m_RequestedDestinations.end ()) request = it->second;
{ m_RequestedDestinations.erase(it);
request = it->second; }
m_RequestedDestinations.erase (it); }
} if (request) {
} if (r)
if (request) request->Success(r);
{ else
if (r) request->Fail();
request->Success (r); }
else }
request->Fail ();
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest(const IdentHash &ident) const {
{ std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); auto it = m_RequestedDestinations.find(ident);
auto it = m_RequestedDestinations.find (ident); if (it != m_RequestedDestinations.end())
if (it != m_RequestedDestinations.end ()) return it->second;
return it->second; return nullptr;
return nullptr; }
}
void NetDbRequests::ManageRequests () void NetDbRequests::ManageRequests() {
{ uint64_t ts = i2p::util::GetSecondsSinceEpoch();
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); for (auto it = m_RequestedDestinations.begin(); it != m_RequestedDestinations.end();) {
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) auto &dest = it->second;
{ bool done = false;
auto& dest = it->second; if (ts < dest->GetCreationTime() + 60) // request is worthless after 1 minute
bool done = false; {
if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute if (ts > dest->GetCreationTime() + 5) // no response for 5 seconds
{ {
if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds auto count = dest->GetExcludedPeers().size();
{ if (!dest->IsExploratory() && count < 7) {
auto count = dest->GetExcludedPeers ().size (); auto pool = i2p::tunnel::tunnels.GetExploratoryPool();
if (!dest->IsExploratory () && count < 7) auto outbound = pool->GetNextOutboundTunnel();
{ auto inbound = pool->GetNextInboundTunnel();
auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); auto nextFloodfill = netdb.GetClosestFloodfill(dest->GetDestination(),
auto outbound = pool->GetNextOutboundTunnel (); dest->GetExcludedPeers());
auto inbound = pool->GetNextInboundTunnel (); if (nextFloodfill && outbound && inbound)
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); outbound->SendTunnelDataMsg(nextFloodfill->GetIdentHash(), 0,
if (nextFloodfill && outbound && inbound) dest->CreateRequestMessage(nextFloodfill, inbound));
outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, else {
dest->CreateRequestMessage (nextFloodfill, inbound)); done = true;
else if (!inbound) LogPrint(eLogWarning, "NetDbReq: No inbound tunnels");
{ if (!outbound) LogPrint(eLogWarning, "NetDbReq: No outbound tunnels");
done = true; if (!nextFloodfill) LogPrint(eLogWarning, "NetDbReq: No more floodfills");
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels"); }
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels"); } else {
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills"); if (!dest->IsExploratory())
} LogPrint(eLogWarning, "NetDbReq: ", dest->GetDestination().ToBase64(),
} " not found after 7 attempts");
else done = true;
{ }
if (!dest->IsExploratory ()) }
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); } else // delete obsolete request
done = true; done = true;
}
}
}
else // delete obsolete request
done = true;
if (done) if (done)
it = m_RequestedDestinations.erase (it); it = m_RequestedDestinations.erase(it);
else else
++it; ++it;
} }
} }
} }
} }

View file

@ -15,62 +15,76 @@
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data class RequestedDestination {
{ public:
class RequestedDestination
{
public:
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete; typedef std::function<void(std::shared_ptr<RouterInfo>)> RequestComplete;
RequestedDestination (const IdentHash& destination, bool isExploratory = false): RequestedDestination(const IdentHash &destination, bool isExploratory = false) :
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; m_Destination(destination), m_IsExploratory(isExploratory), m_CreationTime(0) {};
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
const IdentHash& GetDestination () const { return m_Destination; }; ~RequestedDestination() { if (m_RequestComplete) m_RequestComplete(nullptr); };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
bool IsExploratory () const { return m_IsExploratory; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; }; const IdentHash &GetDestination() const { return m_Destination; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r);
void Fail ();
private: int GetNumExcludedPeers() const { return m_ExcludedPeers.size(); };
IdentHash m_Destination; const std::set<IdentHash> &GetExcludedPeers() { return m_ExcludedPeers; };
bool m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime;
RequestComplete m_RequestComplete;
};
class NetDbRequests void ClearExcludedPeers();
{
public:
void Start (); bool IsExploratory() const { return m_IsExploratory; };
void Stop ();
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); bool IsExcluded(const IdentHash &ident) const { return m_ExcludedPeers.count(ident); };
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
void ManageRequests ();
private: uint64_t GetCreationTime() const { return m_CreationTime; };
mutable std::mutex m_RequestedDestinationsMutex; std::shared_ptr<I2NPMessage> CreateRequestMessage(std::shared_ptr<const RouterInfo>,
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations; std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
};
} std::shared_ptr<I2NPMessage> CreateRequestMessage(const IdentHash &floodfill);
void SetRequestComplete(const RequestComplete &requestComplete) { m_RequestComplete = requestComplete; };
bool IsRequestComplete() const { return m_RequestComplete != nullptr; };
void Success(std::shared_ptr<RouterInfo> r);
void Fail();
private:
IdentHash m_Destination;
bool m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime;
RequestComplete m_RequestComplete;
};
class NetDbRequests {
public:
void Start();
void Stop();
std::shared_ptr<RequestedDestination> CreateRequest(const IdentHash &destination, bool isExploratory,
RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestComplete(const IdentHash &ident, std::shared_ptr<RouterInfo> r);
std::shared_ptr<RequestedDestination> FindRequest(const IdentHash &ident) const;
void ManageRequests();
private:
mutable std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
};
}
} }
#endif #endif

View file

@ -9,17 +9,14 @@
#include "Poly1305.h" #include "Poly1305.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto void Poly1305HMAC(uint64_t *out, const uint64_t *key, const uint8_t *buf, std::size_t sz) {
{ Poly1305 p(key);
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz) p.Update(buf, sz);
{ p.Finish(out);
Poly1305 p(key); }
p.Update(buf, sz); }
p.Finish(out);
}
}
} }
#endif #endif

View file

@ -8,253 +8,229 @@
#ifndef LIBI2PD_POLY1305_H #ifndef LIBI2PD_POLY1305_H
#define LIBI2PD_POLY1305_H #define LIBI2PD_POLY1305_H
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include "Crypto.h" #include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto const std::size_t POLY1305_DIGEST_BYTES = 16;
{ const std::size_t POLY1305_DIGEST_DWORDS = 4;
const std::size_t POLY1305_DIGEST_BYTES = 16; const std::size_t POLY1305_KEY_BYTES = 32;
const std::size_t POLY1305_DIGEST_DWORDS = 4; const std::size_t POLY1305_KEY_DWORDS = 8;
const std::size_t POLY1305_KEY_BYTES = 32; const std::size_t POLY1305_BLOCK_BYTES = 16;
const std::size_t POLY1305_KEY_DWORDS = 8;
const std::size_t POLY1305_BLOCK_BYTES = 16;
namespace poly1305 namespace poly1305 {
{ struct LongBlock {
struct LongBlock unsigned long data[17];
{
unsigned long data[17];
operator unsigned long * ()
{
return data;
}
};
struct Block operator unsigned long *() {
{ return data;
unsigned char data[17]; }
};
void Zero() struct Block {
{ unsigned char data[17];
memset(data, 0, sizeof(data));
}
operator uint8_t * () void Zero() {
{ memset(data, 0, sizeof(data));
return data; }
}
Block & operator += (const Block & other) operator uint8_t *() {
{ return data;
unsigned short u; }
unsigned int i;
for(u = 0, i = 0; i < 17; i++)
{
u += (unsigned short) data[i] + (unsigned short) other.data[i];
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
return *this;
}
Block & operator %=(const LongBlock & other) Block &operator+=(const Block &other) {
{ unsigned short u;
unsigned long u; unsigned int i;
unsigned int i; for (u = 0, i = 0; i < 17; i++) {
u = 0; u += (unsigned short) data[i] + (unsigned short) other.data[i];
for (i = 0; i < 16; i++) { data[i] = (unsigned char) u & 0xff;
u += other.data[i]; u >>= 8;
data[i] = (unsigned char)u & 0xff; }
u >>= 8; return *this;
} }
u += other.data[16];
data[16] = (unsigned char)u & 0x03;
u >>= 2;
u += (u << 2);
for (i = 0; i < 16; i++) {
u += data[i];
data[i] = (unsigned char)u & 0xff;
u >>= 8;
}
data[16] += (unsigned char)u;
return *this;
}
Block & operator = (const Block & other) Block &operator%=(const LongBlock &other) {
{ unsigned long u;
memcpy(data, other.data, sizeof(data)); unsigned int i;
return *this; u = 0;
} for (i = 0; i < 16; i++) {
u += other.data[i];
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
u += other.data[16];
data[16] = (unsigned char) u & 0x03;
u >>= 2;
u += (u << 2);
for (i = 0; i < 16; i++) {
u += data[i];
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
data[16] += (unsigned char) u;
return *this;
}
Block & operator ~ () Block &operator=(const Block &other) {
{ memcpy(data, other.data, sizeof(data));
static const Block minusp = { return *this;
0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xfc
};
Block orig;
unsigned char neg;
unsigned int i;
orig = *this;
*this += minusp;
neg = -(data[16] >> 7);
for(i = 0; i < 17; i++)
data[i] ^= neg & (orig.data[i] ^ data[i]);
return *this; Block &operator~() {
} static const Block minusp = {
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc
};
Block orig;
unsigned char neg;
unsigned int i;
orig = *this;
*this += minusp;
neg = -(data[16] >> 7);
for (i = 0; i < 17; i++)
data[i] ^= neg & (orig.data[i] ^ data[i]);
void PutKey(const uint64_t * key_l) return *this;
{ }
const uint8_t * key = (const uint8_t*) key_l;
data[0] = key[0] & 0xff;
data[1] = key[1] & 0xff;
data[2] = key[2] & 0xff;
data[3] = key[3] & 0x0f;
data[4] = key[4] & 0xfc;
data[5] = key[5] & 0xff;
data[6] = key[6] & 0xff;
data[7] = key[7] & 0x0f;
data[8] = key[8] & 0xfc;
data[9] = key[9] & 0xff;
data[10] = key[10] & 0xff;
data[11] = key[11] & 0x0f;
data[12] = key[12] & 0xfc;
data[13] = key[13] & 0xff;
data[14] = key[14] & 0xff;
data[15] = key[15] & 0x0f;
data[16] = 0;
}
template<typename Int_t> void PutKey(const uint64_t *key_l) {
void Put(const Int_t * d, uint8_t last=0) const uint8_t *key = (const uint8_t *) key_l;
{ data[0] = key[0] & 0xff;
memcpy(data, d, 16); data[1] = key[1] & 0xff;
data[16] = last; data[2] = key[2] & 0xff;
} data[3] = key[3] & 0x0f;
}; data[4] = key[4] & 0xfc;
data[5] = key[5] & 0xff;
data[6] = key[6] & 0xff;
data[7] = key[7] & 0x0f;
data[8] = key[8] & 0xfc;
data[9] = key[9] & 0xff;
data[10] = key[10] & 0xff;
data[11] = key[11] & 0x0f;
data[12] = key[12] & 0xfc;
data[13] = key[13] & 0xff;
data[14] = key[14] & 0xff;
data[15] = key[15] & 0x0f;
data[16] = 0;
}
struct Buffer template<typename Int_t>
{ void Put(const Int_t *d, uint8_t last = 0) {
uint8_t data[POLY1305_BLOCK_BYTES]; memcpy(data, d, 16);
data[16] = last;
}
};
operator uint8_t * () struct Buffer {
{ uint8_t data[POLY1305_BLOCK_BYTES];
return data;
}
};
}
struct Poly1305 operator uint8_t *() {
{ return data;
Poly1305(const uint64_t * key) }
{ };
m_Leftover = 0; }
m_H.Zero();
m_Final = 0;
m_R.PutKey(key);
m_Pad.Put(key + 2);
}
void Update(const uint8_t * buf, size_t sz) struct Poly1305 {
{ Poly1305(const uint64_t *key) {
// process leftover m_Leftover = 0;
if(m_Leftover) m_H.Zero();
{ m_Final = 0;
size_t want = POLY1305_BLOCK_BYTES - m_Leftover; m_R.PutKey(key);
if(want > sz) want = sz; m_Pad.Put(key + 2);
memcpy(m_Buffer + m_Leftover, buf, want); }
sz -= want;
buf += want;
m_Leftover += want;
if(m_Leftover < POLY1305_BLOCK_BYTES) return;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
m_Leftover = 0;
}
// process blocks
if(sz >= POLY1305_BLOCK_BYTES)
{
size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1));
Blocks(buf, want);
buf += want;
sz -= want;
}
// leftover
if(sz)
{
memcpy(m_Buffer+m_Leftover, buf, sz);
m_Leftover += sz;
}
}
void Blocks(const uint8_t * buf, size_t sz) void Update(const uint8_t *buf, size_t sz) {
{ // process leftover
const unsigned char hi = m_Final ^ 1; if (m_Leftover) {
while (sz >= POLY1305_BLOCK_BYTES) { size_t want = POLY1305_BLOCK_BYTES - m_Leftover;
unsigned long u; if (want > sz) want = sz;
unsigned int i, j; memcpy(m_Buffer + m_Leftover, buf, want);
m_Msg.Put(buf, hi); sz -= want;
/* h += m */ buf += want;
m_H += m_Msg; m_Leftover += want;
if (m_Leftover < POLY1305_BLOCK_BYTES) return;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
m_Leftover = 0;
}
// process blocks
if (sz >= POLY1305_BLOCK_BYTES) {
size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1));
Blocks(buf, want);
buf += want;
sz -= want;
}
// leftover
if (sz) {
memcpy(m_Buffer + m_Leftover, buf, sz);
m_Leftover += sz;
}
}
/* h *= r */ void Blocks(const uint8_t *buf, size_t sz) {
for (i = 0; i < 17; i++) { const unsigned char hi = m_Final ^ 1;
u = 0; while (sz >= POLY1305_BLOCK_BYTES) {
for (j = 0; j <= i ; j++) { unsigned long u;
u += (unsigned short)m_H.data[j] * m_R.data[i - j]; unsigned int i, j;
} m_Msg.Put(buf, hi);
for (j = i + 1; j < 17; j++) { /* h += m */
unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j]; m_H += m_Msg;
v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
u += v;
}
m_HR[i] = u;
}
/* (partial) h %= p */
m_H %= m_HR;
buf += POLY1305_BLOCK_BYTES;
sz -= POLY1305_BLOCK_BYTES;
}
}
void Finish(uint64_t * out) /* h *= r */
{ for (i = 0; i < 17; i++) {
// process leftovers u = 0;
if(m_Leftover) for (j = 0; j <= i; j++) {
{ u += (unsigned short) m_H.data[j] * m_R.data[i - j];
size_t idx = m_Leftover; }
m_Buffer[idx++] = 1; for (j = i + 1; j < 17; j++) {
for(; idx < POLY1305_BLOCK_BYTES; idx++) unsigned long v = (unsigned short) m_H.data[j] * m_R.data[i + 17 - j];
m_Buffer[idx] = 0; v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
m_Final = 1; u += v;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES); }
} m_HR[i] = u;
}
/* (partial) h %= p */
m_H %= m_HR;
buf += POLY1305_BLOCK_BYTES;
sz -= POLY1305_BLOCK_BYTES;
}
}
// freeze H void Finish(uint64_t *out) {
~m_H; // process leftovers
// add pad if (m_Leftover) {
m_H += m_Pad; size_t idx = m_Leftover;
// copy digest m_Buffer[idx++] = 1;
memcpy(out, m_H, 16); for (; idx < POLY1305_BLOCK_BYTES; idx++)
} m_Buffer[idx] = 0;
m_Final = 1;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
}
size_t m_Leftover; // freeze H
poly1305::Buffer m_Buffer; ~m_H;
poly1305::Block m_H; // add pad
poly1305::Block m_R; m_H += m_Pad;
poly1305::Block m_Pad; // copy digest
poly1305::Block m_Msg; memcpy(out, m_H, 16);
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); size_t m_Leftover;
} poly1305::Buffer m_Buffer;
poly1305::Block m_H;
poly1305::Block m_R;
poly1305::Block m_Pad;
poly1305::Block m_Msg;
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 #endif

View file

@ -14,190 +14,165 @@
#include "Log.h" #include "Log.h"
#include "Profiling.h" #include "Profiling.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
{
i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
RouterProfile::RouterProfile (): RouterProfile::RouterProfile() :
m_LastUpdateTime (boost::posix_time::second_clock::local_time()), m_LastUpdateTime(boost::posix_time::second_clock::local_time()),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), m_NumTunnelsAgreed(0), m_NumTunnelsDeclined(0), m_NumTunnelsNonReplied(0),
m_NumTimesTaken (0), m_NumTimesRejected (0) m_NumTimesTaken(0), m_NumTimesRejected(0) {
{ }
}
boost::posix_time::ptime RouterProfile::GetTime () const boost::posix_time::ptime RouterProfile::GetTime() const {
{ return boost::posix_time::second_clock::local_time();
return boost::posix_time::second_clock::local_time(); }
}
void RouterProfile::UpdateTime () void RouterProfile::UpdateTime() {
{ m_LastUpdateTime = GetTime();
m_LastUpdateTime = GetTime (); }
}
void RouterProfile::Save (const IdentHash& identHash) void RouterProfile::Save(const IdentHash &identHash) {
{ // fill sections
// fill sections boost::property_tree::ptree participation;
boost::property_tree::ptree participation; participation.put(PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed); participation.put(PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined); participation.put(PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied); boost::property_tree::ptree usage;
boost::property_tree::ptree usage; usage.put(PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); usage.put(PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); // fill property tree
// fill property tree boost::property_tree::ptree pt;
boost::property_tree::ptree pt; pt.put(PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string(m_LastUpdateTime));
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime)); pt.put_child(PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation); pt.put_child(PEER_PROFILE_SECTION_USAGE, usage);
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
// save to file // save to file
std::string ident = identHash.ToBase64 (); std::string ident = identHash.ToBase64();
std::string path = m_ProfilesStorage.Path(ident); std::string path = m_ProfilesStorage.Path(ident);
try { try {
boost::property_tree::write_ini (path, pt); boost::property_tree::write_ini(path, pt);
} catch (std::exception& ex) { } catch (std::exception &ex) {
/* boost exception verbose enough */ /* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ()); LogPrint(eLogError, "Profiling: ", ex.what());
} }
} }
void RouterProfile::Load (const IdentHash& identHash) void RouterProfile::Load(const IdentHash &identHash) {
{ std::string ident = identHash.ToBase64();
std::string ident = identHash.ToBase64 (); std::string path = m_ProfilesStorage.Path(ident);
std::string path = m_ProfilesStorage.Path(ident); boost::property_tree::ptree pt;
boost::property_tree::ptree pt;
if (!i2p::fs::Exists(path)) if (!i2p::fs::Exists(path)) {
{ LogPrint(eLogWarning, "Profiling: No profile yet for ", ident);
LogPrint(eLogWarning, "Profiling: No profile yet for ", ident); return;
return; }
}
try try {
{ boost::property_tree::read_ini(path, pt);
boost::property_tree::read_ini (path, pt); } catch (std::exception &ex) {
} catch (std::exception& ex) /* boost exception verbose enough */
{ LogPrint(eLogError, "Profiling: ", ex.what());
/* boost exception verbose enough */ return;
LogPrint (eLogError, "Profiling: ", ex.what ()); }
return;
}
try try {
{ auto t = pt.get(PEER_PROFILE_LAST_UPDATE_TIME, "");
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); if (t.length() > 0)
if (t.length () > 0) m_LastUpdateTime = boost::posix_time::time_from_string(t);
m_LastUpdateTime = boost::posix_time::time_from_string (t); if ((GetTime() - m_LastUpdateTime).hours() < PEER_PROFILE_EXPIRATION_TIMEOUT) {
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) try {
{ // read participations
try auto participations = pt.get_child(PEER_PROFILE_SECTION_PARTICIPATION);
{ m_NumTunnelsAgreed = participations.get(PEER_PROFILE_PARTICIPATION_AGREED, 0);
// read participations m_NumTunnelsDeclined = participations.get(PEER_PROFILE_PARTICIPATION_DECLINED, 0);
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); m_NumTunnelsNonReplied = participations.get(PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); }
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); catch (boost::property_tree::ptree_bad_path &ex) {
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); LogPrint(eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION,
} " in profile for ", ident);
catch (boost::property_tree::ptree_bad_path& ex) }
{ try {
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident); // read usage
} auto usage = pt.get_child(PEER_PROFILE_SECTION_USAGE);
try m_NumTimesTaken = usage.get(PEER_PROFILE_USAGE_TAKEN, 0);
{ m_NumTimesRejected = usage.get(PEER_PROFILE_USAGE_REJECTED, 0);
// read usage }
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); catch (boost::property_tree::ptree_bad_path &ex) {
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); LogPrint(eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE,
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); " in profile for ", ident);
} }
catch (boost::property_tree::ptree_bad_path& ex) } else
{ *this = RouterProfile();
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident); }
} catch (std::exception &ex) {
} LogPrint(eLogError, "Profiling: Can't read profile ", ident, " :", ex.what());
else }
*this = RouterProfile (); }
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret) void RouterProfile::TunnelBuildResponse(uint8_t ret) {
{ UpdateTime();
UpdateTime (); if (ret > 0)
if (ret > 0) m_NumTunnelsDeclined++;
m_NumTunnelsDeclined++; else
else m_NumTunnelsAgreed++;
m_NumTunnelsAgreed++; }
}
void RouterProfile::TunnelNonReplied () void RouterProfile::TunnelNonReplied() {
{ m_NumTunnelsNonReplied++;
m_NumTunnelsNonReplied++; UpdateTime();
UpdateTime (); }
}
bool RouterProfile::IsLowPartcipationRate () const bool RouterProfile::IsLowPartcipationRate() const {
{ return 4 * m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate }
}
bool RouterProfile::IsLowReplyRate () const bool RouterProfile::IsLowReplyRate() const {
{ auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined; return m_NumTunnelsNonReplied > 10 * (total + 1);
return m_NumTunnelsNonReplied > 10*(total + 1); }
}
bool RouterProfile::IsBad () bool RouterProfile::IsBad() {
{ auto isBad = IsAlwaysDeclining() || IsLowPartcipationRate() /*|| IsLowReplyRate ()*/;
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; if (isBad && m_NumTimesRejected > 10 * (m_NumTimesTaken + 1)) {
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) // reset profile
{ m_NumTunnelsAgreed = 0;
// reset profile m_NumTunnelsDeclined = 0;
m_NumTunnelsAgreed = 0; m_NumTunnelsNonReplied = 0;
m_NumTunnelsDeclined = 0; isBad = false;
m_NumTunnelsNonReplied = 0; }
isBad = false; if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
} return isBad;
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++; }
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash) std::shared_ptr<RouterProfile> GetRouterProfile(const IdentHash &identHash) {
{ auto profile = std::make_shared<RouterProfile>();
auto profile = std::make_shared<RouterProfile> (); profile->Load(identHash); // if possible
profile->Load (identHash); // if possible return profile;
return profile; }
}
void InitProfilesStorage () void InitProfilesStorage() {
{ m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); }
}
void DeleteObsoleteProfiles () void DeleteObsoleteProfiles() {
{ struct stat st;
struct stat st; std::time_t now = std::time(nullptr);
std::time_t now = std::time(nullptr);
std::vector<std::string> files; std::vector<std::string> files;
m_ProfilesStorage.Traverse(files); m_ProfilesStorage.Traverse(files);
for (const auto& path: files) { for (const auto &path: files) {
if (stat(path.c_str(), &st) != 0) { if (stat(path.c_str(), &st) != 0) {
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path); LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue; continue;
} }
if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) { if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) {
LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path); LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path);
i2p::fs::Remove(path); i2p::fs::Remove(path);
} }
} }
} }
} }
} }

View file

@ -13,65 +13,70 @@
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data // sections
{ const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
// sections const char PEER_PROFILE_SECTION_USAGE[] = "usage";
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; // params
const char PEER_PROFILE_SECTION_USAGE[] = "usage"; const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
// params const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed"; const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied";
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined"; const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied"; const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days) const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day) const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
class RouterProfile class RouterProfile {
{ public:
public:
RouterProfile (); RouterProfile();
RouterProfile& operator= (const RouterProfile& ) = default;
void Save (const IdentHash& identHash); RouterProfile &operator=(const RouterProfile &) = default;
void Load (const IdentHash& identHash);
bool IsBad (); void Save(const IdentHash &identHash);
void TunnelBuildResponse (uint8_t ret); void Load(const IdentHash &identHash);
void TunnelNonReplied ();
private: bool IsBad();
boost::posix_time::ptime GetTime () const; void TunnelBuildResponse(uint8_t ret);
void UpdateTime ();
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; void TunnelNonReplied();
bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const;
private: private:
boost::posix_time::ptime m_LastUpdateTime; boost::posix_time::ptime GetTime() const;
// participation
uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined;
uint32_t m_NumTunnelsNonReplied;
// usage
uint32_t m_NumTimesTaken;
uint32_t m_NumTimesRejected;
};
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash); void UpdateTime();
void InitProfilesStorage ();
void DeleteObsoleteProfiles (); bool IsAlwaysDeclining() const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
}
bool IsLowPartcipationRate() const;
bool IsLowReplyRate() const;
private:
boost::posix_time::ptime m_LastUpdateTime;
// participation
uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined;
uint32_t m_NumTunnelsNonReplied;
// usage
uint32_t m_NumTimesTaken;
uint32_t m_NumTimesRejected;
};
std::shared_ptr<RouterProfile> GetRouterProfile(const IdentHash &identHash);
void InitProfilesStorage();
void DeleteObsoleteProfiles();
}
} }
#endif #endif

View file

@ -17,117 +17,100 @@
#include <functional> #include <functional>
#include <utility> #include <utility>
namespace i2p namespace i2p {
{ namespace util {
namespace util template<typename Element>
{ class Queue {
template<typename Element> public:
class Queue
{
public:
void Put (Element e) void Put(Element e) {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); m_Queue.push(std::move(e));
m_Queue.push (std::move(e)); m_NonEmpty.notify_one();
m_NonEmpty.notify_one (); }
}
template<template<typename, typename...>class Container, typename... R> template<template<typename, typename...> class Container, typename... R>
void Put (const Container<Element, R...>& vec) void Put(const Container<Element, R...> &vec) {
{ if (!vec.empty()) {
if (!vec.empty ()) std::unique_lock <std::mutex> l(m_QueueMutex);
{ for (const auto &it: vec)
std::unique_lock<std::mutex> l(m_QueueMutex); m_Queue.push(std::move(it));
for (const auto& it: vec) m_NonEmpty.notify_one();
m_Queue.push (std::move(it)); }
m_NonEmpty.notify_one (); }
}
}
Element GetNext () Element GetNext() {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); auto el = GetNonThreadSafe();
auto el = GetNonThreadSafe (); if (!el) {
if (!el) m_NonEmpty.wait(l);
{ el = GetNonThreadSafe();
m_NonEmpty.wait (l); }
el = GetNonThreadSafe (); return el;
} }
return el;
}
Element GetNextWithTimeout (int usec) Element GetNextWithTimeout(int usec) {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); auto el = GetNonThreadSafe();
auto el = GetNonThreadSafe (); if (!el) {
if (!el) m_NonEmpty.wait_for(l, std::chrono::milliseconds(usec));
{ el = GetNonThreadSafe();
m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec)); }
el = GetNonThreadSafe (); return el;
} }
return el;
}
void Wait () void Wait() {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); m_NonEmpty.wait(l);
m_NonEmpty.wait (l); }
}
bool Wait (int sec, int usec) bool Wait(int sec, int usec) {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); return m_NonEmpty.wait_for(l, std::chrono::seconds(sec) + std::chrono::milliseconds(usec)) !=
return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout; std::cv_status::timeout;
} }
bool IsEmpty () bool IsEmpty() {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); return m_Queue.empty();
return m_Queue.empty (); }
}
int GetSize () int GetSize() {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); return m_Queue.size();
return m_Queue.size (); }
}
void WakeUp () { m_NonEmpty.notify_all (); }; void WakeUp() { m_NonEmpty.notify_all(); };
Element Get () Element Get() {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); return GetNonThreadSafe();
return GetNonThreadSafe (); }
}
Element Peek () Element Peek() {
{ std::unique_lock <std::mutex> l(m_QueueMutex);
std::unique_lock<std::mutex> l(m_QueueMutex); return GetNonThreadSafe(true);
return GetNonThreadSafe (true); }
}
private: private:
Element GetNonThreadSafe (bool peek = false) Element GetNonThreadSafe(bool peek = false) {
{ if (!m_Queue.empty()) {
if (!m_Queue.empty ()) auto el = m_Queue.front();
{ if (!peek)
auto el = m_Queue.front (); m_Queue.pop();
if (!peek) return el;
m_Queue.pop (); }
return el; return nullptr;
} }
return nullptr;
}
private: private:
std::queue<Element> m_Queue; std::queue <Element> m_Queue;
std::mutex m_QueueMutex; std::mutex m_QueueMutex;
std::condition_variable m_NonEmpty; std::condition_variable m_NonEmpty;
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -16,46 +16,52 @@
#include "Identity.h" #include "Identity.h"
#include "Crypto.h" #include "Crypto.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
class Reseeder class Reseeder {
{ typedef Tag<512> PublicKey;
typedef Tag<512> PublicKey;
public: public:
Reseeder(); Reseeder();
~Reseeder();
void Bootstrap ();
int ReseedFromServers ();
int ProcessSU3File (const char * filename);
int ProcessZIPFile (const char * filename);
void LoadCertificates (); ~Reseeder();
private: void Bootstrap();
int ReseedFromSU3Url (const std::string& url, bool isHttps = true); int ReseedFromServers();
void LoadCertificate (const std::string& filename);
int ProcessSU3Stream (std::istream& s); int ProcessSU3File(const char *filename);
int ProcessZIPStream (std::istream& s, uint64_t contentLength);
bool FindZipDataDescriptor (std::istream& s); int ProcessZIPFile(const char *filename);
std::string HttpsRequest (const std::string& address); void LoadCertificates();
std::string YggdrasilRequest (const std::string& address);
template<typename Stream>
std::string ReseedRequest (Stream& s, const std::string& uri);
private: private:
std::map<std::string, PublicKey> m_SigningKeys; int ReseedFromSU3Url(const std::string &url, bool isHttps = true);
};
} void LoadCertificate(const std::string &filename);
int ProcessSU3Stream(std::istream &s);
int ProcessZIPStream(std::istream &s, uint64_t contentLength);
bool FindZipDataDescriptor(std::istream &s);
std::string HttpsRequest(const std::string &address);
std::string YggdrasilRequest(const std::string &address);
template<typename Stream>
std::string ReseedRequest(Stream &s, const std::string &uri);
private:
std::map<std::string, PublicKey> m_SigningKeys;
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -19,200 +19,265 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p {
{ namespace garlic {
namespace garlic class RouterIncomingRatchetSession;
{ }
class RouterIncomingRatchetSession;
}
const char ROUTER_INFO[] = "router.info"; const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys"; const char ROUTER_KEYS[] = "router.keys";
const char NTCP2_KEYS[] = "ntcp2.keys"; const char NTCP2_KEYS[] = "ntcp2.keys";
const char SSU2_KEYS[] = "ssu2.keys"; const char SSU2_KEYS[] = "ssu2.keys";
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
enum RouterStatus enum RouterStatus {
{ eRouterStatusOK = 0,
eRouterStatusOK = 0, eRouterStatusTesting = 1,
eRouterStatusTesting = 1, eRouterStatusFirewalled = 2,
eRouterStatusFirewalled = 2, eRouterStatusError = 3,
eRouterStatusError = 3, eRouterStatusUnknown = 4,
eRouterStatusUnknown = 4, eRouterStatusProxy = 5,
eRouterStatusProxy = 5, eRouterStatusMesh = 6
eRouterStatusMesh = 6 };
};
enum RouterError enum RouterError {
{ eRouterErrorNone = 0,
eRouterErrorNone = 0, eRouterErrorClockSkew = 1,
eRouterErrorClockSkew = 1, eRouterErrorOffline = 2,
eRouterErrorOffline = 2, eRouterErrorSymmetricNAT = 3
eRouterErrorSymmetricNAT = 3 };
};
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext : public i2p::garlic::GarlicDestination {
{ private:
private:
struct NTCP2PrivateKeys struct NTCP2PrivateKeys {
{ uint8_t staticPublicKey[32];
uint8_t staticPublicKey[32]; uint8_t staticPrivateKey[32];
uint8_t staticPrivateKey[32]; uint8_t iv[16];
uint8_t iv[16]; };
};
struct SSU2PrivateKeys struct SSU2PrivateKeys {
{ uint8_t staticPublicKey[32];
uint8_t staticPublicKey[32]; uint8_t staticPrivateKey[32];
uint8_t staticPrivateKey[32]; uint8_t intro[32];
uint8_t intro[32]; };
};
public: public:
RouterContext (); RouterContext();
void Init ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void Init();
i2p::data::LocalRouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo ()
{
return std::shared_ptr<i2p::data::RouterInfo> (&m_RouterInfo,
[](i2p::data::RouterInfo *) {});
}
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
[](i2p::garlic::GarlicDestination *) {});
}
const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; const i2p::data::PrivateKeys &GetPrivateKeys() const { return m_Keys; };
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& GetNTCP2StaticKeys ();
const uint8_t * GetSSU2StaticPublicKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; }; i2p::data::LocalRouterInfo &GetRouterInfo() { return m_RouterInfo; };
const uint8_t * GetSSU2StaticPrivateKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; };
const uint8_t * GetSSU2IntroKey () const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; };
i2p::crypto::X25519Keys& GetSSU2StaticKeys ();
uint32_t GetUptime () const; // in seconds std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo() {
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; return std::shared_ptr<i2p::data::RouterInfo>(&m_RouterInfo,
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; }; [](i2p::data::RouterInfo *) {});
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; }; }
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
void SetStatusSSU2 (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
RouterStatus GetStatusV6 () const { return m_StatusV6; };
void SetStatusV6 (RouterStatus status);
void SetStatusV6SSU2 (RouterStatus status);
RouterError GetErrorV6 () const { return m_ErrorV6; };
void SetErrorV6 (RouterError error) { m_StatusV6 = eRouterStatusError; m_ErrorV6 = error; };
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
void UpdatePort (int port); // called from Daemon std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination() {
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon return std::shared_ptr<i2p::garlic::GarlicDestination>(this,
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg); [](i2p::garlic::GarlicDestination *) {});
void UpdateNTCP2Address (bool enable); }
void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
void UpdateSSU2Address (bool enable);
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
void RemoveSSUAddress (); // delete SSU address for older routers
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
void ClearSSU2Introducers (bool v4);
bool IsUnreachable () const;
void SetUnreachable (bool v4, bool v6);
void SetUnreachableSSU2 (bool v4, bool v6);
void SetReachable (bool v4, bool v6);
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
void SetFamily (const std::string& family);
std::string GetFamily () const;
void SetBandwidth (int limit); /* in kilobytes */
void SetBandwidth (char L); /* by letter */
void SetShareRatio (int percents); // 0 - 100
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
bool SupportsMesh () const { return m_RouterInfo.IsMesh (); };
void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4);
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
void SetMTU (int mtu, bool v4);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove const uint8_t *GetNTCP2StaticPublicKey() const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
void UpdateStats ();
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
void CleanupDestination (); // garlic destination
// implements LocalDestination const uint8_t *GetNTCP2StaticPrivateKey() const {
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr;
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
void SetLeaseSetUpdated () {};
// implements GarlicDestination const uint8_t *GetNTCP2IV() const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
// override GarlicDestination i2p::crypto::X25519Keys &GetNTCP2StaticKeys();
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
protected: const uint8_t *GetSSU2StaticPublicKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; };
// implements GarlicDestination const uint8_t *GetSSU2StaticPrivateKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; };
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
private: const uint8_t *GetSSU2IntroKey() const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; };
void CreateNewRouter (); i2p::crypto::X25519Keys &GetSSU2StaticKeys();
void NewRouterInfo ();
void UpdateRouterInfo ();
void NewNTCP2Keys ();
void NewSSU2Keys ();
bool IsSSU2Only () const; // SSU2 and no SSU
bool Load ();
void SaveKeys ();
uint16_t SelectRandomPort () const;
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize); uint32_t GetUptime() const; // in seconds
uint64_t GetLastUpdateTime() const { return m_LastUpdateTime; };
private: uint64_t GetBandwidthLimit() const { return m_BandwidthLimit; };
i2p::data::LocalRouterInfo m_RouterInfo; uint64_t GetTransitBandwidthLimit() const { return (m_BandwidthLimit * m_ShareRatio) / 100LL; };
i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status, m_StatusV6;
RouterError m_Error, m_ErrorV6;
int m_NetID;
std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
std::unique_ptr<SSU2PrivateKeys> m_SSU2Keys;
std::unique_ptr<i2p::crypto::X25519Keys> m_NTCP2StaticKeys, m_SSU2StaticKeys;
// for ECIESx25519
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
};
extern RouterContext context; RouterStatus GetStatus() const { return m_Status; };
void SetStatus(RouterStatus status);
void SetStatusSSU2(RouterStatus status);
RouterError GetError() const { return m_Error; };
void SetError(RouterError error) {
m_Status = eRouterStatusError;
m_Error = error;
};
RouterStatus GetStatusV6() const { return m_StatusV6; };
void SetStatusV6(RouterStatus status);
void SetStatusV6SSU2(RouterStatus status);
RouterError GetErrorV6() const { return m_ErrorV6; };
void SetErrorV6(RouterError error) {
m_StatusV6 = eRouterStatusError;
m_ErrorV6 = error;
};
int GetNetID() const { return m_NetID; };
void SetNetID(int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord(const uint8_t *encrypted, uint8_t *data);
bool DecryptTunnelShortRequestRecord(const uint8_t *encrypted, uint8_t *data);
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, bool v4, bool v6, bool ygg);
void UpdateNTCP2Address(bool enable);
void PublishSSU2Address(int port, bool publish, bool v4, bool v6);
void UpdateSSU2Address(bool enable);
void RemoveNTCPAddress(bool v4only = true); // delete NTCP address for older routers. TODO: remove later
void RemoveSSUAddress(); // delete SSU address for older routers
bool AddIntroducer(const i2p::data::RouterInfo::Introducer &introducer);
void RemoveIntroducer(const boost::asio::ip::udp::endpoint &e);
bool AddSSU2Introducer(const i2p::data::RouterInfo::Introducer &introducer, bool v4);
void RemoveSSU2Introducer(const i2p::data::IdentHash &h, bool v4);
void ClearSSU2Introducers(bool v4);
bool IsUnreachable() const;
void SetUnreachable(bool v4, bool v6);
void SetUnreachableSSU2(bool v4, bool v6);
void SetReachable(bool v4, bool v6);
bool IsFloodfill() const { return m_IsFloodfill; };
void SetFloodfill(bool floodfill);
void SetFamily(const std::string &family);
std::string GetFamily() const;
void SetBandwidth(int limit); /* in kilobytes */
void SetBandwidth(char L); /* by letter */
void SetShareRatio(int percents); // 0 - 100
bool AcceptsTunnels() const { return m_AcceptsTunnels; };
void SetAcceptsTunnels(bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
bool SupportsV6() const { return m_RouterInfo.IsV6(); };
bool SupportsV4() const { return m_RouterInfo.IsV4(); };
bool SupportsMesh() const { return m_RouterInfo.IsMesh(); };
void SetSupportsV6(bool supportsV6);
void SetSupportsV4(bool supportsV4);
void SetSupportsMesh(bool supportsmesh, const boost::asio::ip::address_v6 &host);
void SetMTU(int mtu, bool v4);
i2p::crypto::NoiseSymmetricState &GetCurrentNoiseState() { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address(const boost::asio::ip::address &host); // called from Daemon. TODO: remove
void UpdateStats();
void UpdateTimestamp(uint64_t ts); // in seconds, called from NetDb before publishing
void CleanupDestination(); // garlic destination
// implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity() const { return m_Keys.GetPublic(); };
bool Decrypt(const uint8_t *encrypted, uint8_t *data, i2p::data::CryptoKeyType preferredCrypto) const;
void Sign(const uint8_t *buf, int len, uint8_t *signature) const { m_Keys.Sign(buf, len, signature); };
void SetLeaseSetUpdated() {};
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet() { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const;
// override GarlicDestination
void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
protected:
// implements GarlicDestination
void HandleI2NPMessage(const uint8_t *buf, size_t len);
bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID);
private:
void CreateNewRouter();
void NewRouterInfo();
void UpdateRouterInfo();
void NewNTCP2Keys();
void NewSSU2Keys();
bool IsSSU2Only() const; // SSU2 and no SSU
bool Load();
void SaveKeys();
uint16_t SelectRandomPort() const;
bool DecryptECIESTunnelBuildRecord(const uint8_t *encrypted, uint8_t *data, size_t clearTextSize);
private:
i2p::data::LocalRouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status, m_StatusV6;
RouterError m_Error, m_ErrorV6;
int m_NetID;
std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
std::unique_ptr<SSU2PrivateKeys> m_SSU2Keys;
std::unique_ptr<i2p::crypto::X25519Keys> m_NTCP2StaticKeys, m_SSU2StaticKeys;
// for ECIESx25519
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
};
extern RouterContext context;
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -21,319 +21,410 @@
#include "Profiling.h" #include "Profiling.h"
#include "Family.h" #include "Family.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
{ const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version";
const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; const char ROUTER_INFO_PROPERTY_FAMILY[] = "family";
const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version"; const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig";
const char ROUTER_INFO_PROPERTY_FAMILY[] = "family";
const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig";
const char CAPS_FLAG_FLOODFILL = 'f'; const char CAPS_FLAG_FLOODFILL = 'f';
const char CAPS_FLAG_HIDDEN = 'H'; const char CAPS_FLAG_HIDDEN = 'H';
const char CAPS_FLAG_REACHABLE = 'R'; const char CAPS_FLAG_REACHABLE = 'R';
const char CAPS_FLAG_UNREACHABLE = 'U'; const char CAPS_FLAG_UNREACHABLE = 'U';
/* bandwidth flags */ /* bandwidth flags */
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; /* 48-64 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; /* 48-64 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; /* 64-128 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; /* 64-128 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; /* 128-256 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; /* 128-256 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
const char CAPS_FLAG_V4 = '4'; const char CAPS_FLAG_V4 = '4';
const char CAPS_FLAG_V6 = '6'; const char CAPS_FLAG_V6 = '6';
const char CAPS_FLAG_SSU_TESTING = 'B'; const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C'; const char CAPS_FLAG_SSU_INTRODUCER = 'C';
const uint8_t COST_NTCP2_PUBLISHED = 3; const uint8_t COST_NTCP2_PUBLISHED = 3;
const uint8_t COST_NTCP2_NON_PUBLISHED = 14; const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
const uint8_t COST_SSU2_DIRECT = 8; const uint8_t COST_SSU2_DIRECT = 8;
const uint8_t COST_SSU_DIRECT = 9; const uint8_t COST_SSU_DIRECT = 9;
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11; const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
const uint8_t COST_SSU2_NON_PUBLISHED = 15; const uint8_t COST_SSU2_NON_PUBLISHED = 15;
const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later
class RouterInfo: public RoutingDestination class RouterInfo : public RoutingDestination {
{ public:
public:
enum SupportedTransports enum SupportedTransports {
{ eNTCP2V4 = 0x01,
eNTCP2V4 = 0x01, eNTCP2V6 = 0x02,
eNTCP2V6 = 0x02, eSSUV4 = 0x04,
eSSUV4 = 0x04, eSSUV6 = 0x08,
eSSUV6 = 0x08, eNTCP2V6Mesh = 0x10,
eNTCP2V6Mesh = 0x10, eSSU2V4 = 0x20,
eSSU2V4 = 0x20, eSSU2V6 = 0x40,
eSSU2V6 = 0x40, eAllTransports = 0xFF
eAllTransports = 0xFF };
}; typedef uint8_t CompatibleTransports;
typedef uint8_t CompatibleTransports;
enum Caps enum Caps {
{ eFloodfill = 0x01,
eFloodfill = 0x01, eHighBandwidth = 0x02,
eHighBandwidth = 0x02, eExtraBandwidth = 0x04,
eExtraBandwidth = 0x04, eReachable = 0x08,
eReachable = 0x08, eHidden = 0x10,
eHidden = 0x10, eUnreachable = 0x20
eUnreachable = 0x20 };
};
enum AddressCaps enum AddressCaps {
{ eV4 = 0x01,
eV4 = 0x01, eV6 = 0x02,
eV6 = 0x02, eSSUTesting = 0x04,
eSSUTesting = 0x04, eSSUIntroducer = 0x08
eSSUIntroducer = 0x08 };
};
enum TransportStyle enum TransportStyle {
{ eTransportUnknown = 0,
eTransportUnknown = 0, eTransportNTCP,
eTransportNTCP, eTransportSSU,
eTransportSSU, eTransportSSU2
eTransportSSU2 };
};
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer struct Introducer {
{ Introducer() : iPort(0), iExp(0) {};
Introducer (): iPort (0), iExp (0) {}; boost::asio::ip::address iHost;
boost::asio::ip::address iHost; int iPort;
int iPort; IntroKey iKey; // or ih for SSU2
IntroKey iKey; // or ih for SSU2 uint32_t iTag;
uint32_t iTag; uint32_t iExp;
uint32_t iExp; };
};
struct SSUExt struct SSUExt {
{ int mtu;
int mtu; std::vector<Introducer> introducers;
std::vector<Introducer> introducers; };
};
struct Address struct Address {
{ TransportStyle transportStyle;
TransportStyle transportStyle; boost::asio::ip::address host;
boost::asio::ip::address host; Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU
Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU int port;
int port; uint64_t date;
uint64_t date; uint8_t caps;
uint8_t caps; bool published = false;
bool published = false; std::unique_ptr<SSUExt> ssu; // not null for SSU
std::unique_ptr<SSUExt> ssu; // not null for SSU
bool IsCompatible (const boost::asio::ip::address& other) const bool IsCompatible(const boost::asio::ip::address &other) const {
{ return (IsV4() && other.is_v4()) ||
return (IsV4 () && other.is_v4 ()) || (IsV6() && other.is_v6());
(IsV6 () && other.is_v6 ()); }
}
bool operator==(const Address& other) const bool operator==(const Address &other) const {
{ return transportStyle == other.transportStyle &&
return transportStyle == other.transportStyle && host == other.host && port == other.port;
host == other.host && port == other.port; }
}
bool operator!=(const Address& other) const bool operator!=(const Address &other) const {
{ return !(*this == other);
return !(*this == other); }
}
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; }; bool IsNTCP2() const { return transportStyle == eTransportNTCP; };
bool IsSSU2 () const { return transportStyle == eTransportSSU2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); };
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsSSU2() const { return transportStyle == eTransportSSU2; };
bool IsPeerTesting () const { return caps & eSSUTesting; };
bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; bool IsPublishedNTCP2() const { return IsNTCP2() && published; };
bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); };
};
class Buffer: public std::array<uint8_t, MAX_RI_BUFFER_SIZE> bool IsReachableSSU() const { return (bool) ssu && (published || UsesIntroducer()); };
{
public:
Buffer () = default; bool UsesIntroducer() const { return (bool) ssu && !ssu->introducers.empty(); };
Buffer (const uint8_t * buf, size_t len);
};
typedef std::vector<std::shared_ptr<Address> > Addresses; bool IsIntroducer() const { return caps & eSSUIntroducer; };
RouterInfo (const std::string& fullPath); bool IsPeerTesting() const { return caps & eSSUTesting; };
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len);
RouterInfo (const uint8_t * buf, size_t len);
virtual ~RouterInfo ();
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; }; bool IsV4() const { return (caps & AddressCaps::eV4) || (host.is_v4() && !host.is_unspecified()); };
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; };
virtual void SetProperty (const std::string& key, const std::string& value) {};
virtual void ClearProperties () {};
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
std::shared_ptr<const Address> GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetSSUV6Address () const;
std::shared_ptr<const Address> GetYggdrasilAddress () const;
std::shared_ptr<const Address> GetSSU2V4Address () const;
std::shared_ptr<const Address> GetSSU2V6Address () const;
std::shared_ptr<const Address> GetSSU2Address (bool v4) const;
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); bool IsV6() const { return (caps & AddressCaps::eV6) || (host.is_v6() && !host.is_unspecified()); };
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, };
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps = 0); // non published
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
const boost::asio::ip::address& host, int port); // published
bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
void UpdateSupportedTransports ();
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
bool IsSSU (bool v4only = true) const;
bool IsSSUV6 () const { return m_SupportedTransports & eSSUV6; };
bool IsNTCP2 (bool v4only = true) const;
bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; };
bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; };
bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; };
bool IsV6 () const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; };
void EnableV6 ();
void DisableV6 ();
void EnableV4 ();
void DisableV4 ();
void EnableMesh ();
void DisableMesh ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; };
bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; };
CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; };
bool HasValidAddresses () const { return m_SupportedTransports; };
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const;
bool IsPeerTesting (bool v4) const;
bool IsSSU2PeerTesting (bool v4) const;
bool IsIntroducer (bool v4) const;
bool IsSSU2Introducer (bool v4) const;
uint8_t GetCaps () const { return m_Caps; }; class Buffer : public std::array<uint8_t, MAX_RI_BUFFER_SIZE> {
void SetCaps (uint8_t caps) { m_Caps = caps; }; public:
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; Buffer() = default;
bool IsUnreachable () const { return m_IsUnreachable; };
const uint8_t * GetBuffer () const { return m_Buffer->data (); }; Buffer(const uint8_t *buf, size_t len);
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary };
size_t GetBufferLen () const { return m_BufferLen; };
bool IsUpdated () const { return m_IsUpdated; }; typedef std::vector<std::shared_ptr<Address> > Addresses;
void SetUpdated (bool updated) { m_IsUpdated = updated; };
bool SaveToFile (const std::string& fullPath);
std::shared_ptr<RouterProfile> GetProfile () const; RouterInfo(const std::string &fullPath);
void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); };
void Update (const uint8_t * buf, size_t len); RouterInfo(const RouterInfo &) = default;
void DeleteBuffer () { 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 */ RouterInfo &operator=(const RouterInfo &) = default;
bool IsFamily (FamilyID famid) const;
// implements RoutingDestination RouterInfo(std::shared_ptr<Buffer> &&buf, size_t len);
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
bool IsDestination () const { return false; }; RouterInfo(const uint8_t *buf, size_t len);
protected: virtual ~RouterInfo();
RouterInfo (); std::shared_ptr<const IdentityEx> GetRouterIdentity() const { return m_RouterIdentity; };
uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; };
void UpdateBuffer (const uint8_t * buf, size_t len);
void SetBufferLen (size_t len) { m_BufferLen = len; };
void RefreshTimestamp ();
const Addresses& GetAddresses () const { return *m_Addresses; };
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
private: void SetRouterIdentity(std::shared_ptr<const IdentityEx> identity);
bool LoadFile (const std::string& fullPath); std::string GetIdentHashBase64() const { return GetIdentHash().ToBase64(); };
void ReadFromFile (const std::string& fullPath);
void ReadFromStream (std::istream& s);
void ReadFromBuffer (bool verifySignature);
size_t ReadString (char* str, size_t len, std::istream& s) const;
void ExtractCaps (const char * value);
uint8_t ExtractAddressCaps (const char * value) const;
template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer () const;
private: uint64_t GetTimestamp() const { return m_Timestamp; };
FamilyID m_FamilyID; int GetVersion() const { return m_Version; };
std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
bool m_IsUpdated, m_IsUnreachable;
CompatibleTransports m_SupportedTransports, m_ReachableTransports;
uint8_t m_Caps;
int m_Version;
mutable std::shared_ptr<RouterProfile> m_Profile;
};
class LocalRouterInfo: public RouterInfo virtual void SetProperty(const std::string &key, const std::string &value) {};
{
public:
LocalRouterInfo () = default; virtual void ClearProperties() {};
void CreateBuffer (const PrivateKeys& privateKeys);
void UpdateCaps (uint8_t caps);
void SetProperty (const std::string& key, const std::string& value) override; Addresses &
void DeleteProperty (const std::string& key); GetAddresses() { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::string GetProperty (const std::string& key) const; std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey(const uint8_t *key) const;
void ClearProperties () override { m_Properties.clear (); };
bool AddSSU2Introducer (const Introducer& introducer, bool v4); std::shared_ptr<const Address> GetSSU2AddressWithStaticKey(const uint8_t *key, bool isV6) const;
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
private:
void WriteToStream (std::ostream& s) const; std::shared_ptr<const Address> GetPublishedNTCP2V4Address() const;
void UpdateCapsProperty ();
void WriteString (const std::string& str, std::ostream& s) const;
std::shared_ptr<Buffer> NewBuffer () const override;
private: std::shared_ptr<const Address> GetPublishedNTCP2V6Address() const;
std::map<std::string, std::string> m_Properties; std::shared_ptr<const Address> GetSSUAddress(bool v4only = true) const;
};
} std::shared_ptr<const Address> GetSSUV6Address() const;
std::shared_ptr<const Address> GetYggdrasilAddress() const;
std::shared_ptr<const Address> GetSSU2V4Address() const;
std::shared_ptr<const Address> GetSSU2V6Address() const;
std::shared_ptr<const Address> GetSSU2Address(bool v4) const;
void AddSSUAddress(const char *host, int port, const uint8_t *key, int mtu = 0);
void AddNTCP2Address(const uint8_t *staticKey, const uint8_t *iv,
const boost::asio::ip::address &host = boost::asio::ip::address(), int port = 0,
uint8_t caps = 0);
void AddSSU2Address(const uint8_t *staticKey, const uint8_t *introKey, uint8_t caps = 0); // non published
void AddSSU2Address(const uint8_t *staticKey, const uint8_t *introKey,
const boost::asio::ip::address &host, int port); // published
bool AddIntroducer(const Introducer &introducer);
bool RemoveIntroducer(const boost::asio::ip::udp::endpoint &e);
void SetUnreachableAddressesTransportCaps(uint8_t transports); // bitmask of AddressCaps
void UpdateSupportedTransports();
bool IsFloodfill() const { return m_Caps & Caps::eFloodfill; };
bool IsReachable() const { return m_Caps & Caps::eReachable; };
bool IsECIES() const {
return m_RouterIdentity->GetCryptoKeyType() == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
};
bool IsSSU(bool v4only = true) const;
bool IsSSUV6() const { return m_SupportedTransports & eSSUV6; };
bool IsNTCP2(bool v4only = true) const;
bool IsNTCP2V6() const { return m_SupportedTransports & eNTCP2V6; };
bool IsSSU2V4() const { return m_SupportedTransports & eSSU2V4; };
bool IsSSU2V6() const { return m_SupportedTransports & eSSU2V6; };
bool IsV6() const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
bool IsV4() const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
bool IsMesh() const { return m_SupportedTransports & eNTCP2V6Mesh; };
void EnableV6();
void DisableV6();
void EnableV4();
void DisableV4();
void EnableMesh();
void DisableMesh();
bool IsCompatible(const RouterInfo &other) const {
return m_SupportedTransports & other.m_SupportedTransports;
};
bool IsReachableFrom(const RouterInfo &other) const {
return m_ReachableTransports & other.m_SupportedTransports;
};
bool IsReachableBy(CompatibleTransports transports) const { return m_ReachableTransports & transports; };
CompatibleTransports GetCompatibleTransports(bool incoming) const {
return incoming ? m_ReachableTransports : m_SupportedTransports;
};
bool HasValidAddresses() const { return m_SupportedTransports; };
bool IsHidden() const { return m_Caps & eHidden; };
bool IsHighBandwidth() const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth() const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill() const;
bool IsPeerTesting(bool v4) const;
bool IsSSU2PeerTesting(bool v4) const;
bool IsIntroducer(bool v4) const;
bool IsSSU2Introducer(bool v4) const;
uint8_t GetCaps() const { return m_Caps; };
void SetCaps(uint8_t caps) { m_Caps = caps; };
void SetUnreachable(bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable() const { return m_IsUnreachable; };
const uint8_t *GetBuffer() const { return m_Buffer->data(); };
const uint8_t *LoadBuffer(const std::string &fullPath); // load if necessary
size_t GetBufferLen() const { return m_BufferLen; };
bool IsUpdated() const { return m_IsUpdated; };
void SetUpdated(bool updated) { m_IsUpdated = updated; };
bool SaveToFile(const std::string &fullPath);
std::shared_ptr<RouterProfile> GetProfile() const;
void SaveProfile() { if (m_Profile) m_Profile->Save(GetIdentHash()); };
void Update(const uint8_t *buf, size_t len);
void DeleteBuffer() { 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(FamilyID famid) const;
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity() const { return m_RouterIdentity; };
void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
bool IsDestination() const { return false; };
protected:
RouterInfo();
uint8_t *GetBufferPointer(size_t offset = 0) { return m_Buffer->data() + offset; };
void UpdateBuffer(const uint8_t *buf, size_t len);
void SetBufferLen(size_t len) { m_BufferLen = len; };
void RefreshTimestamp();
const Addresses &GetAddresses() const { return *m_Addresses; };
CompatibleTransports GetReachableTransports() const { return m_ReachableTransports; };
void SetReachableTransports(CompatibleTransports transports) { m_ReachableTransports = transports; };
private:
bool LoadFile(const std::string &fullPath);
void ReadFromFile(const std::string &fullPath);
void ReadFromStream(std::istream &s);
void ReadFromBuffer(bool verifySignature);
size_t ReadString(char *str, size_t len, std::istream &s) const;
void ExtractCaps(const char *value);
uint8_t ExtractAddressCaps(const char *value) const;
template<typename Filter>
std::shared_ptr<const Address> GetAddress(Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer() const;
private:
FamilyID m_FamilyID;
std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
boost::shared_ptr <Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
bool m_IsUpdated, m_IsUnreachable;
CompatibleTransports m_SupportedTransports, m_ReachableTransports;
uint8_t m_Caps;
int m_Version;
mutable std::shared_ptr<RouterProfile> m_Profile;
};
class LocalRouterInfo : public RouterInfo {
public:
LocalRouterInfo() = default;
void CreateBuffer(const PrivateKeys &privateKeys);
void UpdateCaps(uint8_t caps);
void SetProperty(const std::string &key, const std::string &value) override;
void DeleteProperty(const std::string &key);
std::string GetProperty(const std::string &key) const;
void ClearProperties() override { m_Properties.clear(); };
bool AddSSU2Introducer(const Introducer &introducer, bool v4);
bool RemoveSSU2Introducer(const IdentHash &h, bool v4);
private:
void WriteToStream(std::ostream &s) const;
void UpdateCapsProperty();
void WriteString(const std::string &str, std::ostream &s) const;
std::shared_ptr<Buffer> NewBuffer() const override;
private:
std::map<std::string, std::string> m_Properties;
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -25,135 +25,185 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "SSUSession.h" #include "SSUSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
{ const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes const size_t SSU_MAX_NUM_INTRODUCERS = 3;
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU_MAX_NUM_INTRODUCERS = 3; const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
struct SSUPacket struct SSUPacket {
{ i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size boost::asio::ip::udp::endpoint from;
boost::asio::ip::udp::endpoint from; size_t len;
size_t len; };
};
class SSUServer class SSUServer {
{ public:
public:
SSUServer (int port); SSUServer(int port);
~SSUServer ();
void Start ();
void Stop ();
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Service; }; ~SSUServer();
i2p::util::MemoryPool<Fragment>& GetFragmentsPool () { return m_FragmentsPool; };
i2p::util::MemoryPool<IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<SentMessage>& GetSentMessagesPool () { return m_SentMessagesPool; };
uint16_t GetPort () const { return m_Endpoint.port (); }; void Start();
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); void Stop();
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay (uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr); bool CreateSession(std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false,
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); bool v4only = false);
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce);
private: bool CreateSession(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
void OpenSocket (); void CreateDirectSession(std::shared_ptr<const i2p::data::RouterInfo> router,
void OpenSocketV6 (); boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
void Run ();
void RunReceivers ();
void RunReceiversV6 ();
void Receive ();
void ReceiveV6 ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, std::shared_ptr<SSUSession> FindSession(const boost::asio::ip::udp::endpoint &e) const;
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
std::list<std::shared_ptr<SSUSession> > FindIntroducers (int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash>& excluded); std::shared_ptr<SSUSession> GetRandomEstablishedV4Session(std::shared_ptr<const SSUSession> excluded);
void ScheduleIntroducersUpdateTimer ();
void ScheduleIntroducersUpdateTimerV6 ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void SchedulePeerTestsCleanupTimer (); std::shared_ptr<SSUSession> GetRandomEstablishedV6Session(std::shared_ptr<const SSUSession> excluded);
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
// timer void DeleteSession(std::shared_ptr<SSUSession> session);
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleTerminationV6 ();
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
private: void DeleteAllSessions();
struct PeerTest boost::asio::io_service &GetService() { return m_Service; };
{
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
volatile bool m_IsRunning; i2p::util::MemoryPool<Fragment> &GetFragmentsPool() { return m_FragmentsPool; };
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6,
m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6;
bool m_IsSyncClockFromPeers;
std::list<boost::asio::ip::udp::endpoint> m_Introducers, m_IntroducersV6; // introducers we are connected to
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
i2p::util::MemoryPool<Fragment> m_FragmentsPool; i2p::util::MemoryPool<IncompleteMessage> &GetIncompleteMessagesPool() { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<IncompleteMessage> m_IncompleteMessagesPool;
i2p::util::MemoryPool<SentMessage> m_SentMessagesPool;
i2p::util::MemoryPoolMt<SSUPacket> m_PacketsPool;
public: i2p::util::MemoryPool<SentMessage> &GetSentMessagesPool() { return m_SentMessagesPool; };
// for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; uint16_t GetPort() const { return m_Endpoint.port(); };
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
}; bool IsSyncClockFromPeers() const { return m_IsSyncClockFromPeers; };
}
void SetLocalAddress(const boost::asio::ip::address &localAddress);
void Send(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &to);
void AddRelay(uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay(uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession(uint32_t tag);
void RescheduleIntroducersUpdateTimer();
void RescheduleIntroducersUpdateTimerV6();
void NewPeerTest(uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
PeerTestParticipant GetPeerTestParticipant(uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession(uint32_t nonce);
void UpdatePeerTest(uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest(uint32_t nonce);
private:
void OpenSocket();
void OpenSocketV6();
void Run();
void RunReceivers();
void RunReceiversV6();
void Receive();
void ReceiveV6();
void HandleReceivedFrom(const boost::system::error_code &ecode, std::size_t bytes_transferred,
SSUPacket *packet);
void HandleReceivedFromV6(const boost::system::error_code &ecode, std::size_t bytes_transferred,
SSUPacket *packet);
void HandleReceivedPackets(std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > *sessions);
void CreateSessionThroughIntroducer(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address,
bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session(Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session(Filter filter);
std::list<std::shared_ptr<SSUSession> >
FindIntroducers(int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash> &excluded);
void ScheduleIntroducersUpdateTimer();
void ScheduleIntroducersUpdateTimerV6();
void HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4);
void SchedulePeerTestsCleanupTimer();
void HandlePeerTestsCleanupTimer(const boost::system::error_code &ecode);
// timer
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
void ScheduleTerminationV6();
void HandleTerminationTimerV6(const boost::system::error_code &ecode);
private:
struct PeerTest {
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
volatile bool m_IsRunning;
std::thread *m_Thread, *m_ReceiversThread, *m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6,
m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6;
bool m_IsSyncClockFromPeers;
std::list<boost::asio::ip::udp::endpoint> m_Introducers, m_IntroducersV6; // introducers we are connected to
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
i2p::util::MemoryPool<Fragment> m_FragmentsPool;
i2p::util::MemoryPool<IncompleteMessage> m_IncompleteMessagesPool;
i2p::util::MemoryPool<SentMessage> m_SentMessagesPool;
i2p::util::MemoryPoolMt<SSUPacket> m_PacketsPool;
public:
// for HTTP only
const decltype(m_Sessions)
&
GetSessions() const { return m_Sessions; };
const decltype(m_SessionsV6)
&
GetSessionsV6() const { return m_SessionsV6; };
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -13,133 +13,169 @@
#include "util.h" #include "util.h"
#include "SSU2Session.h" #include "SSU2Session.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds
{ const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const size_t SSU2_MAX_NUM_INTRODUCERS = 3; const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork
{
struct Packet
{
uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
boost::asio::ip::udp::endpoint from;
};
class ReceiveService: public i2p::util::RunnableService class SSU2Server : private i2p::util::RunnableServiceWithWork {
{ struct Packet {
public: uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
boost::asio::ip::udp::endpoint from;
};
ReceiveService (const std::string& name): RunnableService (name) {}; class ReceiveService : public i2p::util::RunnableService {
boost::asio::io_service& GetService () { return GetIOService (); }; public:
void Start () { StartIOService (); };
void Stop () { StopIOService (); };
};
public: ReceiveService(const std::string &name) : RunnableService(name) {};
SSU2Server (); boost::asio::io_service &GetService() { return GetIOService(); };
~SSU2Server () {};
void Start (); void Start() { StartIOService(); };
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const;
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID);
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
bool AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const;
std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const;
std::shared_ptr<SSU2Session> GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash& excluded) const;
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay (uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag);
void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, void Stop() { StopIOService(); };
const boost::asio::ip::udp::endpoint& to); };
void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to);
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, public:
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
bool StartPeerTest (std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp);
uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const;
uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep);
std::pair<uint64_t, uint32_t> NewIncomingToken (const boost::asio::ip::udp::endpoint& ep);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; }; SSU2Server();
private:
boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint); ~SSU2Server() {};
void Receive (boost::asio::ip::udp::socket& socket);
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
Packet * packet, boost::asio::ip::udp::socket& socket);
void HandleReceivedPacket (Packet * packet);
void HandleReceivedPackets (std::vector<Packet *> packets);
void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void ScheduleTermination (); void Start();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleResend (); void Stop();
void HandleResendTimer (const boost::system::error_code& ecode);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session); boost::asio::io_service &GetService() { return GetIOService(); };
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6 ();
private:
ReceiveService m_ReceiveService; void SetLocalAddress(const boost::asio::ip::address &localAddress);
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers
bool m_IsSyncClockFromPeers;
public:
// for HTTP/I2PControl bool IsSupported(const boost::asio::ip::address &addr) const;
const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; };
}; uint16_t GetPort(bool v4) const;
}
bool IsSyncClockFromPeers() const { return m_IsSyncClockFromPeers; };
void AddSession(std::shared_ptr<SSU2Session> session);
void RemoveSession(uint64_t connID);
void AddSessionByRouterHash(std::shared_ptr<SSU2Session> session);
bool AddPendingOutgoingSession(std::shared_ptr<SSU2Session> session);
void RemovePendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep);
std::shared_ptr<SSU2Session> FindSession(const i2p::data::IdentHash &ident) const;
std::shared_ptr<SSU2Session> FindPendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep) const;
std::shared_ptr<SSU2Session> GetRandomSession(i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash &excluded) const;
void AddRelay(uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay(uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession(uint32_t tag);
void Send(const uint8_t *header, size_t headerLen, const uint8_t *payload, size_t payloadLen,
const boost::asio::ip::udp::endpoint &to);
void Send(const uint8_t *header, size_t headerLen, const uint8_t *headerX, size_t headerXLen,
const uint8_t *payload, size_t payloadLen, const boost::asio::ip::udp::endpoint &to);
bool CreateSession(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
bool StartPeerTest(std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
void UpdateOutgoingToken(const boost::asio::ip::udp::endpoint &ep, uint64_t token, uint32_t exp);
uint64_t FindOutgoingToken(const boost::asio::ip::udp::endpoint &ep) const;
uint64_t GetIncomingToken(const boost::asio::ip::udp::endpoint &ep);
std::pair<uint64_t, uint32_t> NewIncomingToken(const boost::asio::ip::udp::endpoint &ep);
void RescheduleIntroducersUpdateTimer();
void RescheduleIntroducersUpdateTimerV6();
i2p::util::MemoryPool<SSU2SentPacket> &GetSentPacketsPool() { return m_SentPacketsPool; };
private:
boost::asio::ip::udp::socket &OpenSocket(const boost::asio::ip::udp::endpoint &localEndpoint);
void Receive(boost::asio::ip::udp::socket &socket);
void HandleReceivedFrom(const boost::system::error_code &ecode, size_t bytes_transferred,
Packet *packet, boost::asio::ip::udp::socket &socket);
void HandleReceivedPacket(Packet *packet);
void HandleReceivedPackets(std::vector<Packet *> packets);
void ProcessNextPacket(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
void ConnectThroughIntroducer(std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers(int maxNumIntroducers,
bool v4,
const std::set<i2p::data::IdentHash> &excluded) const;
void UpdateIntroducers(bool v4);
void ScheduleIntroducersUpdateTimer();
void HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6();
private:
ReceiveService m_ReceiveService;
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers
bool m_IsSyncClockFromPeers;
public:
// for HTTP/I2PControl
const decltype(m_Sessions)
&
GetSSU2Sessions() const { return m_Sessions; };
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -20,340 +20,403 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
{ const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU2_CLOCK_SKEW = 60; // in seconds
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const int SSU2_CLOCK_SKEW = 60; // in seconds const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52 * 60; // for next token block, in seconds
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds const size_t SSU2_MAX_PACKET_SIZE = 1500;
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds const size_t SSU2_MIN_PACKET_SIZE = 1280;
const size_t SSU2_MAX_PACKET_SIZE = 1500; const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds
const size_t SSU2_MIN_PACKET_SIZE = 1280; const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const int SSU2_MAX_NUM_RESENDS = 5; const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets
const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets const size_t SSU2_MIN_RTO = 100; // in milliseconds
const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets const size_t SSU2_MAX_RTO = 2500; // in milliseconds
const size_t SSU2_MIN_RTO = 100; // in milliseconds const float SSU2_kAPPA = 1.8;
const size_t SSU2_MAX_RTO = 2500; // in milliseconds const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages
const float SSU2_kAPPA = 1.8; const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
enum SSU2MessageType enum SSU2MessageType {
{ eSSU2SessionRequest = 0,
eSSU2SessionRequest = 0, eSSU2SessionCreated = 1,
eSSU2SessionCreated = 1, eSSU2SessionConfirmed = 2,
eSSU2SessionConfirmed = 2, eSSU2Data = 6,
eSSU2Data = 6, eSSU2PeerTest = 7,
eSSU2PeerTest = 7, eSSU2Retry = 9,
eSSU2Retry = 9, eSSU2TokenRequest = 10,
eSSU2TokenRequest = 10, eSSU2HolePunch = 11
eSSU2HolePunch = 11 };
};
enum SSU2BlockType enum SSU2BlockType {
{ eSSU2BlkDateTime = 0,
eSSU2BlkDateTime = 0, eSSU2BlkOptions, // 1
eSSU2BlkOptions, // 1 eSSU2BlkRouterInfo, // 2
eSSU2BlkRouterInfo, // 2 eSSU2BlkI2NPMessage, // 3
eSSU2BlkI2NPMessage, // 3 eSSU2BlkFirstFragment, // 4
eSSU2BlkFirstFragment, // 4 eSSU2BlkFollowOnFragment, // 5
eSSU2BlkFollowOnFragment, // 5 eSSU2BlkTermination, // 6
eSSU2BlkTermination, // 6 eSSU2BlkRelayRequest, // 7
eSSU2BlkRelayRequest, // 7 eSSU2BlkRelayResponse, // 8
eSSU2BlkRelayResponse, // 8 eSSU2BlkRelayIntro, // 9
eSSU2BlkRelayIntro, // 9 eSSU2BlkPeerTest, // 10
eSSU2BlkPeerTest, // 10 eSSU2BlkNextNonce, // 11
eSSU2BlkNextNonce, // 11 eSSU2BlkAck, // 12
eSSU2BlkAck, // 12 eSSU2BlkAddress, // 13
eSSU2BlkAddress, // 13 eSSU2BlkIntroKey, // 14
eSSU2BlkIntroKey, // 14 eSSU2BlkRelayTagRequest, // 15
eSSU2BlkRelayTagRequest, // 15 eSSU2BlkRelayTag, // 16
eSSU2BlkRelayTag, // 16 eSSU2BlkNewToken, // 17
eSSU2BlkNewToken, // 17 eSSU2BlkPathChallenge, // 18
eSSU2BlkPathChallenge, // 18 eSSU2BlkPathResponse, // 19
eSSU2BlkPathResponse, // 19 eSSU2BlkFirstPacketNumber, // 20
eSSU2BlkFirstPacketNumber, // 20 eSSU2BlkPadding = 254
eSSU2BlkPadding = 254 };
};
enum SSU2SessionState enum SSU2SessionState {
{ eSSU2SessionStateUnknown,
eSSU2SessionStateUnknown, eSSU2SessionStateTokenReceived,
eSSU2SessionStateTokenReceived, eSSU2SessionStateSessionRequestSent,
eSSU2SessionStateSessionRequestSent, eSSU2SessionStateSessionRequestReceived,
eSSU2SessionStateSessionRequestReceived, eSSU2SessionStateSessionCreatedSent,
eSSU2SessionStateSessionCreatedSent, eSSU2SessionStateSessionCreatedReceived,
eSSU2SessionStateSessionCreatedReceived, eSSU2SessionStateSessionConfirmedSent,
eSSU2SessionStateSessionConfirmedSent, eSSU2SessionStateEstablished,
eSSU2SessionStateEstablished, eSSU2SessionStateClosing,
eSSU2SessionStateClosing, eSSU2SessionStateTerminated,
eSSU2SessionStateTerminated, eSSU2SessionStateFailed,
eSSU2SessionStateFailed, eSSU2SessionStateIntroduced,
eSSU2SessionStateIntroduced, eSSU2SessionStatePeerTest,
eSSU2SessionStatePeerTest, eSSU2SessionStatePeerTestReceived, // 5 before 4
eSSU2SessionStatePeerTestReceived, // 5 before 4 eSSU2SessionStateTokenRequestReceived
eSSU2SessionStateTokenRequestReceived };
};
enum SSU2PeerTestCode enum SSU2PeerTestCode {
{ eSSU2PeerTestCodeAccept = 0,
eSSU2PeerTestCodeAccept = 0, eSSU2PeerTestCodeBobReasonUnspecified = 1,
eSSU2PeerTestCodeBobReasonUnspecified = 1, eSSU2PeerTestCodeBobNoCharlieAvailable = 2,
eSSU2PeerTestCodeBobNoCharlieAvailable = 2, eSSU2PeerTestCodeBobLimitExceeded = 3,
eSSU2PeerTestCodeBobLimitExceeded = 3, eSSU2PeerTestCodeBobSignatureFailure = 4,
eSSU2PeerTestCodeBobSignatureFailure = 4, eSSU2PeerTestCodeCharlieReasonUnspecified = 64,
eSSU2PeerTestCodeCharlieReasonUnspecified = 64, eSSU2PeerTestCodeCharlieUnsupportedAddress = 65,
eSSU2PeerTestCodeCharlieUnsupportedAddress = 65, eSSU2PeerTestCodeCharlieLimitExceeded = 66,
eSSU2PeerTestCodeCharlieLimitExceeded = 66, eSSU2PeerTestCodeCharlieSignatureFailure = 67,
eSSU2PeerTestCodeCharlieSignatureFailure = 67, eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68,
eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68, eSSU2PeerTestCodeCharlieAliceIsBanned = 69,
eSSU2PeerTestCodeCharlieAliceIsBanned = 69, eSSU2PeerTestCodeCharlieAliceIsUnknown = 70,
eSSU2PeerTestCodeCharlieAliceIsUnknown = 70, eSSU2PeerTestCodeUnspecified = 128
eSSU2PeerTestCodeUnspecified = 128 };
};
enum SSU2RelayResponseCode enum SSU2RelayResponseCode {
{ eSSU2RelayResponseCodeAccept = 0,
eSSU2RelayResponseCodeAccept = 0, eSSU2RelayResponseCodeBobRelayTagNotFound = 5,
eSSU2RelayResponseCodeBobRelayTagNotFound = 5, eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65,
eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65, eSSU2RelayResponseCodeCharlieSignatureFailure = 67,
eSSU2RelayResponseCodeCharlieSignatureFailure = 67, eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 };
};
enum SSU2TerminationReason enum SSU2TerminationReason {
{ eSSU2TerminationReasonNormalClose = 0,
eSSU2TerminationReasonNormalClose = 0, eSSU2TerminationReasonTerminationReceived = 1,
eSSU2TerminationReasonTerminationReceived = 1, eSSU2TerminationReasonIdleTimeout = 2,
eSSU2TerminationReasonIdleTimeout = 2, eSSU2TerminationReasonRouterShutdown = 3,
eSSU2TerminationReasonRouterShutdown = 3, eSSU2TerminationReasonDataPhaseAEADFailure = 4,
eSSU2TerminationReasonDataPhaseAEADFailure= 4, eSSU2TerminationReasonIncompatibleOptions = 5,
eSSU2TerminationReasonIncompatibleOptions = 5, eSSU2TerminationReasonTncompatibleSignatureType = 6,
eSSU2TerminationReasonTncompatibleSignatureType = 6, eSSU2TerminationReasonClockSkew = 7,
eSSU2TerminationReasonClockSkew = 7, eSSU2TerminationPaddingViolation = 8,
eSSU2TerminationPaddingViolation = 8, eSSU2TerminationReasonAEADFramingError = 9,
eSSU2TerminationReasonAEADFramingError = 9, eSSU2TerminationReasonPayloadFormatError = 10,
eSSU2TerminationReasonPayloadFormatError = 10, eSSU2TerminationReasonSessionRequestError = 11,
eSSU2TerminationReasonSessionRequestError = 11, eSSU2TerminationReasonSessionCreatedError = 12,
eSSU2TerminationReasonSessionCreatedError = 12, eSSU2TerminationReasonSessionConfirmedError = 13,
eSSU2TerminationReasonSessionConfirmedError = 13, eSSU2TerminationReasonTimeout = 14,
eSSU2TerminationReasonTimeout = 14, eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15,
eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15, eSSU2TerminationReasonInvalidS = 16,
eSSU2TerminationReasonInvalidS = 16, eSSU2TerminationReasonBanned = 17,
eSSU2TerminationReasonBanned = 17, eSSU2TerminationReasonBadToken = 18,
eSSU2TerminationReasonBadToken = 18, eSSU2TerminationReasonConnectionLimits = 19,
eSSU2TerminationReasonConnectionLimits = 19, eSSU2TerminationReasonIncompatibleVersion = 20,
eSSU2TerminationReasonIncompatibleVersion = 20, eSSU2TerminationReasonWrongNetID = 21,
eSSU2TerminationReasonWrongNetID = 21, eSSU2TerminationReasonReplacedByNewSession = 22
eSSU2TerminationReasonReplacedByNewSession = 22 };
};
struct SSU2IncompleteMessage
{
struct Fragment
{
uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
bool isLast;
};
std::shared_ptr<I2NPMessage> msg; struct SSU2IncompleteMessage {
int nextFragmentNum; struct Fragment {
uint32_t lastFragmentInsertTime; // in seconds uint8_t buf[SSU2_MAX_PACKET_SIZE];
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments; size_t len;
bool isLast;
};
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); std::shared_ptr<I2NPMessage> msg;
}; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
struct SSU2SentPacket void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize);
{ };
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0;
uint64_t sendTime; // in milliseconds
int numResends = 0;
};
// RouterInfo flags
const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
class SSU2Server; struct SSU2SentPacket {
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session> uint8_t payload[SSU2_MAX_PACKET_SIZE];
{ size_t payloadSize = 0;
union Header uint64_t sendTime; // in milliseconds
{ int numResends = 0;
uint64_t ll[2]; };
uint8_t buf[16];
struct
{
uint64_t connID;
uint32_t packetNum;
uint8_t type;
uint8_t flags[3];
} h;
};
struct HandshakePacket // RouterInfo flags
{ const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
Header header; const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
typedef std::function<void ()> OnEstablished; class SSU2Server;
public: class SSU2Session : public TransportSession, public std::enable_shared_from_this<SSU2Session> {
union Header {
uint64_t ll[2];
uint8_t buf[16];
struct {
uint64_t connID;
uint32_t packetNum;
uint8_t type;
uint8_t flags[3];
} h;
};
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr, struct HandshakePacket {
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr); Header header;
~SSU2Session (); uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE * 2];
size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; typedef std::function<void()> OnEstablished;
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; };
std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress () const { return m_Address; };
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished () const { return m_OnEstablished; };
void Connect (); public:
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message
void SendKeepAlive ();
void RequestTermination (SSU2TerminationReason reason);
void CleanUp (uint64_t ts);
void FlushData ();
void Done () override;
void SendLocalRouterInfo (bool update) override;
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
uint32_t GetRelayTag () const override { return m_RelayTag; };
void Resend (uint64_t ts);
bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID () const { return m_SourceConnID; };
SSU2SessionState GetState () const { return m_State; };
void SetState (SSU2SessionState state) { m_State = state; };
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len); SSU2Session(SSU2Server &server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
bool ProcessSessionCreated (uint8_t * buf, size_t len); std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
bool ProcessSessionConfirmed (uint8_t * buf, size_t len);
bool ProcessRetry (uint8_t * buf, size_t len);
bool ProcessHolePunch (uint8_t * buf, size_t len);
bool ProcessPeerTest (uint8_t * buf, size_t len);
void ProcessData (uint8_t * buf, size_t len);
private: ~SSU2Session();
void Terminate (); void SetRemoteEndpoint(const boost::asio::ip::udp::endpoint &ep) { m_RemoteEndpoint = ep; };
void Established ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue (); // returns true if ack block was sent
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket ();
void ConnectAfterIntroduction ();
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
void SendSessionRequest (uint64_t token = 0); const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() const { return m_RemoteEndpoint; };
void SendSessionCreated (const uint8_t * X);
void SendSessionConfirmed (const uint8_t * Y);
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
void SendTokenRequest ();
void SendRetry ();
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num
void SendQuickAck ();
void SendTermination ();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendPathResponse (const uint8_t * data, size_t len);
void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (const uint8_t * buf, size_t len);
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment (const uint8_t * buf, size_t len);
void HandleFollowOnFragment (const uint8_t * buf, size_t len);
bool ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
void HandleRelayRequest (const uint8_t * buf, size_t len);
void HandleRelayIntro (const uint8_t * buf, size_t len);
void HandleRelayResponse (const uint8_t * buf, size_t len);
void HandlePeerTest (const uint8_t * buf, size_t len);
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports() const { return m_RemoteTransports; };
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateAckBlock (uint8_t * buf, size_t len);
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
private: std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress() const { return m_Address; };
SSU2Server& m_Server; void SetOnEstablished(OnEstablished e) { m_OnEstablished = e; };
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
uint64_t m_DestConnID, m_SourceConnID;
SSU2SessionState m_State;
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
uint32_t m_SendPacketNum, m_ReceivePacketNum;
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived;
size_t m_WindowSize, m_RTT, m_RTO;
uint32_t m_RelayTag; // between Bob and Charlie
OnEstablished m_OnEstablished; // callback from Established
boost::asio::deadline_timer m_ConnectTimer;
SSU2TerminationReason m_TerminationReason;
size_t m_MaxPayloadSize;
};
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) OnEstablished GetOnEstablished() const { return m_OnEstablished; };
{
uint64_t data = 0; void Connect();
i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data);
return data; bool Introduce(std::shared_ptr<SSU2Session> session, uint32_t relayTag);
}
} void WaitForIntroduction();
void SendPeerTest(); // Alice, Data message
void SendKeepAlive();
void RequestTermination(SSU2TerminationReason reason);
void CleanUp(uint64_t ts);
void FlushData();
void Done() override;
void SendLocalRouterInfo(bool update) override;
void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs) override;
uint32_t GetRelayTag() const override { return m_RelayTag; };
void Resend(uint64_t ts);
bool IsEstablished() const { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID() const { return m_SourceConnID; };
SSU2SessionState GetState() const { return m_State; };
void SetState(SSU2SessionState state) { m_State = state; };
bool ProcessFirstIncomingMessage(uint64_t connID, uint8_t *buf, size_t len);
bool ProcessSessionCreated(uint8_t *buf, size_t len);
bool ProcessSessionConfirmed(uint8_t *buf, size_t len);
bool ProcessRetry(uint8_t *buf, size_t len);
bool ProcessHolePunch(uint8_t *buf, size_t len);
bool ProcessPeerTest(uint8_t *buf, size_t len);
void ProcessData(uint8_t *buf, size_t len);
private:
void Terminate();
void Established();
void ScheduleConnectTimer();
void HandleConnectTimer(const boost::system::error_code &ecode);
void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue(); // returns true if ack block was sent
bool SendFragmentedMessage(std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket();
void ConnectAfterIntroduction();
void ProcessSessionRequest(Header &header, uint8_t *buf, size_t len);
void ProcessTokenRequest(Header &header, uint8_t *buf, size_t len);
void SendSessionRequest(uint64_t token = 0);
void SendSessionCreated(const uint8_t *X);
void SendSessionConfirmed(const uint8_t *Y);
void KDFDataPhase(uint8_t *keydata_ab, uint8_t *keydata_ba);
void SendTokenRequest();
void SendRetry();
uint32_t SendData(const uint8_t *buf, size_t len); // returns packet num
void SendQuickAck();
void SendTermination();
void SendHolePunch(uint32_t nonce, const boost::asio::ip::udp::endpoint &ep, const uint8_t *introKey,
uint64_t token);
void SendPeerTest(uint8_t msg, const uint8_t *signedData, size_t signedDataLen,
const uint8_t *introKey); // PeerTest message
void SendPathResponse(const uint8_t *data, size_t len);
void HandlePayload(const uint8_t *buf, size_t len);
void HandleDateTime(const uint8_t *buf, size_t len);
void HandleAck(const uint8_t *buf, size_t len);
void HandleAckRange(uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress(const uint8_t *buf, size_t len);
bool ExtractEndpoint(const uint8_t *buf, size_t size, boost::asio::ip::udp::endpoint &ep);
size_t CreateEndpoint(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress() const;
void AdjustMaxPayloadSize();
RouterStatus GetRouterStatus() const;
void SetRouterStatus(RouterStatus status) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo(const uint8_t *buf, size_t size);
void CreateNonce(uint64_t seqn, uint8_t *nonce);
bool UpdateReceivePacketNum(uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment(const uint8_t *buf, size_t len);
void HandleFollowOnFragment(const uint8_t *buf, size_t len);
bool ConcatOutOfSequenceFragments(std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
void HandleRelayRequest(const uint8_t *buf, size_t len);
void HandleRelayIntro(const uint8_t *buf, size_t len);
void HandleRelayResponse(const uint8_t *buf, size_t len);
void HandlePeerTest(const uint8_t *buf, size_t len);
size_t CreateAddressBlock(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &ep);
size_t CreateRouterInfoBlock(uint8_t *buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateAckBlock(uint8_t *buf, size_t len);
size_t CreatePaddingBlock(uint8_t *buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock(uint8_t *buf, size_t len, std::shared_ptr<I2NPMessage> &&msg);
size_t CreateFirstFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr<I2NPMessage> msg,
uint8_t &fragmentNum, uint32_t msgID);
size_t CreateRelayIntroBlock(uint8_t *buf, size_t len, const uint8_t *introData, size_t introDataLen);
size_t CreateRelayResponseBlock(uint8_t *buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce,
uint64_t token, bool v4);
size_t
CreatePeerTestBlock(uint8_t *buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t *routerHash,
const uint8_t *signedData, size_t signedDataLen);
size_t CreatePeerTestBlock(uint8_t *buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock(uint8_t *buf, size_t len);
private:
SSU2Server &m_Server;
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
uint64_t m_DestConnID, m_SourceConnID;
SSU2SessionState m_State;
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
uint32_t m_SendPacketNum, m_ReceivePacketNum;
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
std::map<uint32_t, std::pair<std::shared_ptr<SSU2Session>, uint64_t> > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair<std::shared_ptr<SSU2Session>, uint64_t> > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived;
size_t m_WindowSize, m_RTT, m_RTO;
uint32_t m_RelayTag; // between Bob and Charlie
OnEstablished m_OnEstablished; // callback from Established
boost::asio::deadline_timer m_ConnectTimer;
SSU2TerminationReason m_TerminationReason;
size_t m_MaxPayloadSize;
};
inline uint64_t CreateHeaderMask(const uint8_t *kh, const uint8_t *nonce) {
uint64_t data = 0;
i2p::crypto::ChaCha20((uint8_t * ) & data, 8, kh, nonce, (uint8_t * ) & data);
return data;
}
}
} }
#endif #endif

View file

@ -13,504 +13,436 @@
#include "SSU.h" #include "SSU.h"
#include "SSUData.h" #include "SSUData.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport void IncompleteMessage::AttachNextFragment(const uint8_t *fragment, size_t fragmentSize) {
{ if (msg->len + fragmentSize > msg->maxLen) {
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) LogPrint(eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
{ auto newMsg = NewI2NPMessage();
if (msg->len + fragmentSize > msg->maxLen) *newMsg = *msg;
{ msg = newMsg;
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough"); }
auto newMsg = NewI2NPMessage (); if (msg->Concat(fragment, fragmentSize) < fragmentSize)
*newMsg = *msg; LogPrint(eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
msg = newMsg; nextFragmentNum++;
} }
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
nextFragmentNum++;
}
SSUData::SSUData (SSUSession& session): SSUData::SSUData(SSUSession &session) :
m_Session (session), m_ResendTimer (session.GetService ()), m_Session(session), m_ResendTimer(session.GetService()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), m_MaxPacketSize(session.IsV6() ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) m_PacketSize(m_MaxPacketSize), m_LastMessageReceivedTime(0) {
{ }
}
SSUData::~SSUData () SSUData::~SSUData() {
{ }
}
void SSUData::Start () void SSUData::Start() {
{ }
}
void SSUData::Stop () void SSUData::Stop() {
{ m_ResendTimer.cancel();
m_ResendTimer.cancel (); m_IncompleteMessages.clear();
m_IncompleteMessages.clear (); m_SentMessages.clear();
m_SentMessages.clear (); m_ReceivedMessages.clear();
m_ReceivedMessages.clear (); }
}
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter) void SSUData::AdjustPacketSize(std::shared_ptr<const i2p::data::RouterInfo> remoteRouter) {
{ if (!remoteRouter) return;
if (!remoteRouter) return; auto ssuAddress = remoteRouter->GetSSUAddress();
auto ssuAddress = remoteRouter->GetSSUAddress (); if (ssuAddress && ssuAddress->ssu->mtu) {
if (ssuAddress && ssuAddress->ssu->mtu) if (m_Session.IsV6())
{ m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
if (m_Session.IsV6 ()) else
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
else if (m_PacketSize > 0) {
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // make sure packet size multiple of 16
if (m_PacketSize > 0) m_PacketSize >>= 4;
{ m_PacketSize <<= 4;
// make sure packet size multiple of 16 if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
m_PacketSize >>= 4; LogPrint(eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
m_PacketSize <<= 4; } else {
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; LogPrint(eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize); m_PacketSize = m_MaxPacketSize;
} }
else }
{ }
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
}
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) void SSUData::UpdatePacketSize(const i2p::data::IdentHash &remoteIdent) {
{ auto routerInfo = i2p::data::netdb.FindRouter(remoteIdent);
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); if (routerInfo)
if (routerInfo) AdjustPacketSize(routerInfo);
AdjustPacketSize (routerInfo); }
}
void SSUData::ProcessSentMessageAck (uint32_t msgID) void SSUData::ProcessSentMessageAck(uint32_t msgID) {
{ auto it = m_SentMessages.find(msgID);
auto it = m_SentMessages.find (msgID); if (it != m_SentMessages.end()) {
if (it != m_SentMessages.end ()) m_SentMessages.erase(it);
{ if (m_SentMessages.empty())
m_SentMessages.erase (it); m_ResendTimer.cancel();
if (m_SentMessages.empty ()) }
m_ResendTimer.cancel (); }
}
}
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) void SSUData::ProcessAcks(uint8_t *&buf, uint8_t flag) {
{ if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) {
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) // explicit ACKs
{ uint8_t numAcks = *buf;
// explicit ACKs buf++;
uint8_t numAcks =*buf; for (int i = 0; i < numAcks; i++)
buf++; ProcessSentMessageAck(bufbe32toh(buf + i * 4));
for (int i = 0; i < numAcks; i++) buf += numAcks * 4;
ProcessSentMessageAck (bufbe32toh (buf+i*4)); }
buf += numAcks*4; if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) {
} // explicit ACK bitfields
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) uint8_t numBitfields = *buf;
{ buf++;
// explicit ACK bitfields for (int i = 0; i < numBitfields; i++) {
uint8_t numBitfields =*buf; uint32_t msgID = bufbe32toh(buf);
buf++; buf += 4; // msgID
for (int i = 0; i < numBitfields; i++) auto it = m_SentMessages.find(msgID);
{ // process individual Ack bitfields
uint32_t msgID = bufbe32toh (buf); bool isNonLast = false;
buf += 4; // msgID int fragment = 0;
auto it = m_SentMessages.find (msgID); do {
// process individual Ack bitfields uint8_t bitfield = *buf;
bool isNonLast = false; isNonLast = bitfield & 0x80;
int fragment = 0; bitfield &= 0x7F; // clear MSB
do if (bitfield && it != m_SentMessages.end()) {
{ int numSentFragments = it->second->fragments.size();
uint8_t bitfield = *buf; // process bits
isNonLast = bitfield & 0x80; uint8_t mask = 0x01;
bitfield &= 0x7F; // clear MSB for (int j = 0; j < 7; j++) {
if (bitfield && it != m_SentMessages.end ()) if (bitfield & mask) {
{ if (fragment < numSentFragments)
int numSentFragments = it->second->fragments.size (); it->second->fragments[fragment] = nullptr;
// process bits }
uint8_t mask = 0x01; fragment++;
for (int j = 0; j < 7; j++) mask <<= 1;
{ }
if (bitfield & mask) }
{ buf++;
if (fragment < numSentFragments) } while (isNonLast);
it->second->fragments[fragment] = nullptr; }
} }
fragment++; }
mask <<= 1;
}
}
buf++;
}
while (isNonLast);
}
}
}
void SSUData::ProcessFragments (uint8_t * buf) void SSUData::ProcessFragments(uint8_t *buf) {
{ uint8_t numFragments = *buf; // number of fragments
uint8_t numFragments = *buf; // number of fragments buf++;
buf++; for (int i = 0; i < numFragments; i++) {
for (int i = 0; i < numFragments; i++) uint32_t msgID = bufbe32toh(buf); // message ID
{ buf += 4;
uint32_t msgID = bufbe32toh (buf); // message ID uint8_t frag[4] = {0};
buf += 4; memcpy(frag + 1, buf, 3);
uint8_t frag[4] = {0}; buf += 3;
memcpy (frag + 1, buf, 3); uint32_t fragmentInfo = bufbe32toh(frag); // fragment info
buf += 3; uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info bool isLast = fragmentInfo & 0x010000; // bit 16
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13 uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
bool isLast = fragmentInfo & 0x010000; // bit 16 if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) {
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 LogPrint(eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) return;
{ }
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
return;
}
// find message with msgID // find message with msgID
auto it = m_IncompleteMessages.find (msgID); auto it = m_IncompleteMessages.find(msgID);
if (it == m_IncompleteMessages.end ()) if (it == m_IncompleteMessages.end()) {
{ // create new message
// create new message auto msg = NewI2NPShortMessage();
auto msg = NewI2NPShortMessage (); msg->len -= I2NP_SHORT_HEADER_SIZE;
msg->len -= I2NP_SHORT_HEADER_SIZE; it = m_IncompleteMessages.insert(std::make_pair(msgID,
it = m_IncompleteMessages.insert (std::make_pair (msgID, m_Session.GetServer().GetIncompleteMessagesPool().AcquireShared(
m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first; std::move(msg)))).first;
} }
auto& incompleteMessage = it->second; auto &incompleteMessage = it->second;
// mark fragment as received // mark fragment as received
if (fragmentNum < 64) if (fragmentNum < 64)
incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum); incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum);
else else
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); LogPrint(eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
// handle current fragment // handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum) if (fragmentNum == incompleteMessage->nextFragmentNum) {
{ // expected fragment
// expected fragment incompleteMessage->AttachNextFragment(buf, fragmentSize);
incompleteMessage->AttachNextFragment (buf, fragmentSize); if (!isLast && !incompleteMessage->savedFragments.empty()) {
if (!isLast && !incompleteMessage->savedFragments.empty ()) // try saved fragments
{ for (auto it1 = incompleteMessage->savedFragments.begin();
// try saved fragments it1 != incompleteMessage->savedFragments.end();) {
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) auto &savedFragment = *it1;
{ if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) {
auto& savedFragment = *it1; incompleteMessage->AttachNextFragment(savedFragment->buf, savedFragment->len);
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) isLast = savedFragment->isLast;
{ incompleteMessage->savedFragments.erase(it1++);
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len); } else
isLast = savedFragment->isLast; break;
incompleteMessage->savedFragments.erase (it1++); }
} if (isLast)
else LogPrint(eLogDebug, "SSU: Message ", msgID, " complete");
break; }
} } else {
if (isLast) if (fragmentNum < incompleteMessage->nextFragmentNum)
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete"); // duplicate fragment
} LogPrint(eLogWarning, "SSU: Duplicate fragment ", (int) fragmentNum, " of message ", msgID,
} ", ignored");
else else {
{ // missing fragment
if (fragmentNum < incompleteMessage->nextFragmentNum) LogPrint(eLogWarning, "SSU: Missing fragments from ", (int) incompleteMessage->nextFragmentNum,
// duplicate fragment " to ", fragmentNum - 1, " of message ", msgID);
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored"); auto savedFragment = m_Session.GetServer().GetFragmentsPool().AcquireShared(fragmentNum, buf,
else fragmentSize,
{ isLast);
// missing fragment if (incompleteMessage->savedFragments.insert(savedFragment).second)
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch();
auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast); else
if (incompleteMessage->savedFragments.insert (savedFragment).second) LogPrint(eLogWarning, "SSU: Fragment ", (int) fragmentNum, " of message ", msgID,
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); " already saved");
else }
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); isLast = false;
} }
isLast = false;
}
if (isLast) if (isLast) {
{ // delete incomplete message
// delete incomplete message auto msg = incompleteMessage->msg;
auto msg = incompleteMessage->msg; incompleteMessage->msg = nullptr;
incompleteMessage->msg = nullptr; m_IncompleteMessages.erase(msgID);
m_IncompleteMessages.erase (msgID); // process message
// process message SendMsgAck(msgID);
SendMsgAck (msgID); msg->FromSSU(msgID);
msg->FromSSU (msgID); if (m_Session.GetState() == eSessionStateEstablished) {
if (m_Session.GetState () == eSessionStateEstablished) if (!m_ReceivedMessages.count(msgID)) {
{ m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch();
if (!m_ReceivedMessages.count (msgID)) m_ReceivedMessages.emplace(msgID, m_LastMessageReceivedTime);
{ if (!msg->IsExpired()) {
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); m_Handler.PutNextMessage(std::move(msg));
m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime); } else
if (!msg->IsExpired ()) LogPrint(eLogDebug, "SSU: message expired");
{ } else
m_Handler.PutNextMessage (std::move (msg)); LogPrint(eLogWarning, "SSU: Message ", msgID, " already received");
} } else {
else // we expect DeliveryStatus
LogPrint (eLogDebug, "SSU: message expired"); if (msg->GetTypeID() == eI2NPDeliveryStatus) {
} LogPrint(eLogDebug, "SSU: session established");
else m_Session.Established();
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received"); } else
} LogPrint(eLogError, "SSU: unexpected message ", (int) msg->GetTypeID());
else }
{ } else
// we expect DeliveryStatus SendFragmentAck(msgID, incompleteMessage->receivedFragmentsBits);
if (msg->GetTypeID () == eI2NPDeliveryStatus) buf += fragmentSize;
{ }
LogPrint (eLogDebug, "SSU: session established"); }
m_Session.Established ();
}
else
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
}
}
else
SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits);
buf += fragmentSize;
}
}
void SSUData::FlushReceivedMessage () void SSUData::FlushReceivedMessage() {
{ m_Handler.Flush();
m_Handler.Flush (); }
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len) void SSUData::ProcessMessage(uint8_t *buf, size_t len) {
{ //uint8_t * start = buf;
//uint8_t * start = buf; uint8_t flag = *buf;
uint8_t flag = *buf; buf++;
buf++; LogPrint(eLogDebug, "SSU: Process data, flags=", (int) flag, ", len=", len);
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len); // process acks if presented
// process acks if presented if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) ProcessAcks(buf, flag);
ProcessAcks (buf, flag); // extended data if presented
// extended data if presented if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) {
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) uint8_t extendedDataSize = *buf;
{ buf++; // size
uint8_t extendedDataSize = *buf; LogPrint(eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
buf++; // size buf += extendedDataSize;
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present"); }
buf += extendedDataSize; // process data
} ProcessFragments(buf);
// process data }
ProcessFragments (buf);
}
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg) void SSUData::Send(std::shared_ptr<i2p::I2NPMessage> msg) {
{ uint32_t msgID = msg->ToSSU();
uint32_t msgID = msg->ToSSU (); if (m_SentMessages.find(msgID) != m_SentMessages.end()) {
if (m_SentMessages.find (msgID) != m_SentMessages.end()) LogPrint(eLogWarning, "SSU: message ", msgID, " already sent");
{ return;
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent"); }
return; if (m_SentMessages.empty()) // schedule resend at first message only
} ScheduleResend();
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ()); auto ret = m_SentMessages.emplace(msgID, m_Session.GetServer().GetSentMessagesPool().AcquireShared());
auto& sentMessage = ret.first->second; auto &sentMessage = ret.first->second;
if (ret.second) if (ret.second) {
{ sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch() + RESEND_INTERVAL;
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; sentMessage->numResends = 0;
sentMessage->numResends = 0; }
} auto &fragments = sentMessage->fragments;
auto& fragments = sentMessage->fragments; size_t payloadSize =
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) m_PacketSize - sizeof(SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength (); size_t len = msg->GetLength();
uint8_t * msgBuf = msg->GetSSUHeader (); uint8_t *msgBuf = msg->GetSSUHeader();
uint32_t fragmentNum = 0; uint32_t fragmentNum = 0;
while (len > 0 && fragmentNum <= 127) while (len > 0 && fragmentNum <= 127) {
{ auto fragment = m_Session.GetServer().GetFragmentsPool().AcquireShared();
auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (); fragment->fragmentNum = fragmentNum;
fragment->fragmentNum = fragmentNum; uint8_t *payload = fragment->buf + sizeof(SSUHeader);
uint8_t * payload = fragment->buf + sizeof (SSUHeader); *payload = DATA_FLAG_WANT_REPLY; // for compatibility
*payload = DATA_FLAG_WANT_REPLY; // for compatibility payload++;
payload++; *payload = 1; // always 1 message fragment per message
*payload = 1; // always 1 message fragment per message payload++;
payload++; htobe32buf(payload, msgID);
htobe32buf (payload, msgID); payload += 4;
payload += 4; bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max size_t size = isLast ? len : payloadSize;
size_t size = isLast ? len : payloadSize; uint32_t fragmentInfo = (fragmentNum << 17);
uint32_t fragmentInfo = (fragmentNum << 17); if (isLast)
if (isLast) fragmentInfo |= 0x010000;
fragmentInfo |= 0x010000;
fragmentInfo |= size; fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo); fragmentInfo = htobe32(fragmentInfo);
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); memcpy(payload, (uint8_t * )(&fragmentInfo) + 1, 3);
payload += 3; payload += 3;
memcpy (payload, msgBuf, size); memcpy(payload, msgBuf, size);
size += payload - fragment->buf; size += payload - fragment->buf;
uint8_t rem = size & 0x0F; uint8_t rem = size & 0x0F;
if (rem) // make sure 16 bytes boundary if (rem) // make sure 16 bytes boundary
{ {
auto padding = 16 - rem; auto padding = 16 - rem;
memset (fragment->buf + size, 0, padding); memset(fragment->buf + size, 0, padding);
size += padding; size += padding;
} }
fragment->len = size; fragment->len = size;
fragments.push_back (fragment); fragments.push_back(fragment);
// encrypt message with session key // encrypt message with session key
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf); m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
try try {
{ m_Session.Send(buf, size);
m_Session.Send (buf, size); }
} catch (boost::system::system_error &ec) {
catch (boost::system::system_error& ec) LogPrint(eLogWarning, "SSU: Can't send data fragment ", ec.what());
{ }
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ()); if (!isLast) {
} len -= payloadSize;
if (!isLast) msgBuf += payloadSize;
{ } else
len -= payloadSize; len = 0;
msgBuf += payloadSize; fragmentNum++;
} }
else }
len = 0;
fragmentNum++;
}
}
void SSUData::SendMsgAck (uint32_t msgID) void SSUData::SendMsgAck(uint32_t msgID) {
{ uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16 uint8_t *payload = buf + sizeof(SSUHeader);
uint8_t * payload = buf + sizeof (SSUHeader); *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag payload++;
payload++; *payload = 1; // number of ACKs
*payload = 1; // number of ACKs payload++;
payload++; htobe32buf(payload, msgID); // msgID
htobe32buf (payload, msgID); // msgID payload += 4;
payload += 4; *payload = 0; // number of fragments
*payload = 0; // number of fragments
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (buf, 48); m_Session.Send(buf, 48);
} }
void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits) void SSUData::SendFragmentAck(uint32_t msgID, uint64_t bits) {
{ if (!bits) return;
if (!bits) return; uint8_t buf[64 + 18] = {0};
uint8_t buf[64 + 18] = {0}; uint8_t *payload = buf + sizeof(SSUHeader);
uint8_t * payload = buf + sizeof (SSUHeader); *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag payload++;
payload++; *payload = 1; // number of ACK bitfields
*payload = 1; // number of ACK bitfields payload++;
payload++; // one ack
// one ack *(uint32_t * )(payload) = htobe32(msgID); // msgID
*(uint32_t *)(payload) = htobe32 (msgID); // msgID payload += 4;
payload += 4; size_t len = 0;
size_t len = 0; while (bits) {
while (bits) *payload = (bits & 0x7F); // next 7 bits
{ bits >>= 7;
*payload = (bits & 0x7F); // next 7 bits if (bits) *payload &= 0x80; // 0x80 means non-last
bits >>= 7; payload++;
if (bits) *payload &= 0x80; // 0x80 means non-last len++;
payload++; len++; }
} *payload = 0; // number of fragments
*payload = 0; // number of fragments len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4 // encrypt message with session key
// encrypt message with session key m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, len);
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); m_Session.Send(buf, len);
m_Session.Send (buf, len); }
}
void SSUData::ScheduleResend() void SSUData::ScheduleResend() {
{ m_ResendTimer.cancel();
m_ResendTimer.cancel (); m_ResendTimer.expires_from_now(boost::posix_time::seconds(RESEND_INTERVAL));
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL)); auto s = m_Session.shared_from_this();
auto s = m_Session.shared_from_this(); m_ResendTimer.async_wait(
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) [s](const boost::system::error_code &ecode) { s->m_Data.HandleResendTimer(ecode); });
{ s->m_Data.HandleResendTimer (ecode); }); }
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode) void SSUData::HandleResendTimer(const boost::system::error_code &ecode) {
{ if (ecode != boost::asio::error::operation_aborted) {
if (ecode != boost::asio::error::operation_aborted) uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
{ uint32_t ts = i2p::util::GetSecondsSinceEpoch();
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; int numResent = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); for (auto it = m_SentMessages.begin(); it != m_SentMessages.end();) {
int numResent = 0; if (ts >= it->second->nextResendTime) {
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) if (it->second->numResends < MAX_NUM_RESENDS) {
{ for (auto &f: it->second->fragments)
if (ts >= it->second->nextResendTime) if (f) {
{ try {
if (it->second->numResends < MAX_NUM_RESENDS) m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
{ m_Session.Send(buf, f->len); // resend
for (auto& f: it->second->fragments) numResent++;
if (f) }
{ catch (boost::system::system_error &ec) {
try LogPrint(eLogWarning, "SSU: Can't resend message ", it->first,
{ " data fragment: ", ec.what());
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf); }
m_Session.Send (buf, f->len); // resend }
numResent++;
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ());
}
}
it->second->numResends++; it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL; it->second->nextResendTime += it->second->numResends * RESEND_INTERVAL;
++it; ++it;
} } else {
else LogPrint(eLogInfo, "SSU: message ", it->first, " has not been ACKed after ",
{ MAX_NUM_RESENDS, " attempts, deleted");
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted"); it = m_SentMessages.erase(it);
it = m_SentMessages.erase (it); }
} } else
} ++it;
else }
++it; if (m_SentMessages.empty()) return; // nothing to resend
} if (numResent < MAX_OUTGOING_WINDOW_SIZE)
if (m_SentMessages.empty ()) return; // nothing to resend ScheduleResend();
if (numResent < MAX_OUTGOING_WINDOW_SIZE) else {
ScheduleResend (); LogPrint(eLogError, "SSU: resend window exceeds max size. Session terminated");
else m_Session.Close();
{ }
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated"); }
m_Session.Close (); }
}
}
}
void SSUData::CleanUp (uint64_t ts) void SSUData::CleanUp(uint64_t ts) {
{ for (auto it = m_IncompleteMessages.begin(); it != m_IncompleteMessages.end();) {
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) {
{ LogPrint(eLogWarning, "SSU: message ", it->first, " was not completed in ",
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
{ it = m_IncompleteMessages.erase(it);
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); } else
it = m_IncompleteMessages.erase (it); ++it;
} }
else
++it;
}
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL) if (m_ReceivedMessages.size() > MAX_NUM_RECEIVED_MESSAGES ||
// decay ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
m_ReceivedMessages.clear (); // decay
else m_ReceivedMessages.clear();
{ else {
// delete old received messages // delete old received messages
for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();) for (auto it = m_ReceivedMessages.begin(); it != m_ReceivedMessages.end();) {
{ if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT) it = m_ReceivedMessages.erase(it);
it = m_ReceivedMessages.erase (it); else
else ++it;
++it; }
} }
} }
} }
}
} }

View file

@ -21,111 +21,118 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport const size_t SSU_MTU_V4 = 1484;
{ const size_t SSU_MTU_V6 = 1488;
const size_t SSU_MTU_V4 = 1484; const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
const size_t SSU_MTU_V6 = 1488; const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 const int RESEND_INTERVAL = 3; // in seconds
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 const int MAX_NUM_RESENDS = 5;
const int RESEND_INTERVAL = 3; // in seconds const int DECAY_INTERVAL = 20; // in seconds
const int MAX_NUM_RESENDS = 5; const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const int DECAY_INTERVAL = 20; // in seconds const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store
const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check // data flags
const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
// data flags const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04; const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
struct Fragment struct Fragment {
{ int fragmentNum;
int fragmentNum; size_t len;
size_t len; bool isLast;
bool isLast; uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
Fragment () = default; Fragment() = default;
Fragment (int n, const uint8_t * b, int l, bool last):
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
};
struct FragmentCmp Fragment(int n, const uint8_t *b, int l, bool last) :
{ fragmentNum(n), len(l), isLast(last) { memcpy(buf, b, len); };
bool operator() (const std::shared_ptr<Fragment>& f1, const std::shared_ptr<Fragment>& f2) const };
{
return f1->fragmentNum < f2->fragmentNum;
};
};
struct IncompleteMessage struct FragmentCmp {
{ bool operator()(const std::shared_ptr<Fragment> &f1, const std::shared_ptr<Fragment> &f2) const {
std::shared_ptr<I2NPMessage> msg; return f1->fragmentNum < f2->fragmentNum;
int nextFragmentNum; };
uint32_t lastFragmentInsertTime; // in seconds };
uint64_t receivedFragmentsBits;
std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (std::shared_ptr<I2NPMessage>&& m): msg (m), nextFragmentNum (0), struct IncompleteMessage {
lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; std::shared_ptr<I2NPMessage> msg;
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); int nextFragmentNum;
}; uint32_t lastFragmentInsertTime; // in seconds
uint64_t receivedFragmentsBits;
std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
struct SentMessage IncompleteMessage(std::shared_ptr<I2NPMessage> &&m) : msg(m), nextFragmentNum(0),
{ lastFragmentInsertTime(0),
std::vector<std::shared_ptr<Fragment> > fragments; receivedFragmentsBits(0) {};
uint32_t nextResendTime; // in seconds
int numResends;
};
class SSUSession; void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize);
class SSUData };
{
public:
SSUData (SSUSession& session); struct SentMessage {
~SSUData (); std::vector<std::shared_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds
int numResends;
};
void Start (); class SSUSession;
void Stop ();
void CleanUp (uint64_t ts);
void ProcessMessage (uint8_t * buf, size_t len); class SSUData {
void FlushReceivedMessage (); public:
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter); SSUData(SSUSession &session);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
private: ~SSUData();
void SendMsgAck (uint32_t msgID); void Start();
void SendFragmentAck (uint32_t msgID, uint64_t bits);
void ProcessAcks (uint8_t *& buf, uint8_t flag);
void ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID);
void ScheduleResend (); void Stop();
void HandleResendTimer (const boost::system::error_code& ecode);
private: void CleanUp(uint64_t ts);
SSUSession& m_Session; void ProcessMessage(uint8_t *buf, size_t len);
std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages; void FlushReceivedMessage();
std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
boost::asio::deadline_timer m_ResendTimer; void Send(std::shared_ptr<i2p::I2NPMessage> msg);
int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler; void AdjustPacketSize(std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
uint32_t m_LastMessageReceivedTime; // in second
}; void UpdatePacketSize(const i2p::data::IdentHash &remoteIdent);
}
private:
void SendMsgAck(uint32_t msgID);
void SendFragmentAck(uint32_t msgID, uint64_t bits);
void ProcessAcks(uint8_t *&buf, uint8_t flag);
void ProcessFragments(uint8_t *buf);
void ProcessSentMessageAck(uint32_t msgID);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
private:
SSUSession &m_Session;
std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages;
std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
boost::asio::deadline_timer m_ResendTimer;
int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler;
uint32_t m_LastMessageReceivedTime; // in second
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -17,161 +17,203 @@
#include "TransportSession.h" #include "TransportSession.h"
#include "SSUData.h" #include "SSUData.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
{
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
struct SSUHeader
{
uint8_t mac[16];
uint8_t iv[16];
uint8_t flag;
uint8_t time[4];
uint8_t GetPayloadType () const { return flag >> 4; }; struct SSUHeader {
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; }; uint8_t mac[16];
}; uint8_t iv[16];
uint8_t flag;
uint8_t time[4];
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds uint8_t GetPayloadType() const { return flag >> 4; };
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU_CLOCK_SKEW = 60; // in seconds
const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768;
// payload types (4 bits) bool IsExtendedOptions() const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; };
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
// extended options const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001; const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU_CLOCK_SKEW = 60; // in seconds
const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768;
enum SessionState // payload types (4 bits)
{ const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
eSessionStateUnknown, const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
eSessionStateIntroduced, const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
eSessionStateEstablished, const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
eSessionStateClosed, const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
eSessionStateFailed const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
}; const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
enum PeerTestParticipant // extended options
{ const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
ePeerTestParticipantUnknown = 0,
ePeerTestParticipantAlice1,
ePeerTestParticipantAlice2,
ePeerTestParticipantBob,
ePeerTestParticipantCharlie
};
class SSUServer; enum SessionState {
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession> eSessionStateUnknown,
{ eSessionStateIntroduced,
public: eSessionStateEstablished,
eSessionStateClosed,
eSessionStateFailed
};
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, enum PeerTestParticipant {
std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, bool peerTest = false); ePeerTestParticipantUnknown = 0,
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); ePeerTestParticipantAlice1,
~SSUSession (); ePeerTestParticipantAlice2,
ePeerTestParticipantBob,
ePeerTestParticipantCharlie
};
void Connect (); class SSUServer;
void WaitForConnect ();
void Introduce (const i2p::data::RouterInfo::Introducer& introducer,
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
void WaitForIntroduction ();
void Close ();
void Done ();
void Failed ();
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
SSUServer& GetServer () { return m_Server; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; class SSUSession : public TransportSession, public std::enable_shared_from_this<SSUSession> {
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs); public:
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; }; SSUSession(SSUServer &server, boost::asio::ip::udp::endpoint &remoteEndpoint,
size_t GetNumSentBytes () const { return m_NumSentBytes; }; std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, bool peerTest = false);
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void SendKeepAlive (); void ProcessNextMessage(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
void FlushData (); ~SSUSession();
void CleanUp (uint64_t ts);
private: void Connect();
boost::asio::io_service& GetService (); void WaitForConnect();
void CreateAESandMacKey (const uint8_t * pubKey);
size_t GetSSUHeaderSize (const uint8_t * buf) const;
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (const uint8_t * buf, size_t len);
void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true);
void ProcessSessionConfirmed (const uint8_t * buf, size_t len);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
void SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from);
void ProcessRelayResponse (const uint8_t * buf, size_t len);
void ProcessRelayIntro (const uint8_t * buf, size_t len);
void Established ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void ProcessData (uint8_t * buf, size_t len);
void SendSessionDestroyed ();
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
void Send (const uint8_t * buf, size_t size);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey, void Introduce(const i2p::data::RouterInfo::Introducer &introducer,
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0); std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key void WaitForIntroduction();
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out); // with session key
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
void Reset (); void Close();
static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size void Done();
private: void Failed();
friend class SSUData; // TODO: change in later const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() { return m_RemoteEndpoint; };
SSUServer& m_Server;
const boost::asio::ip::udp::endpoint m_RemoteEndpoint; SSUServer &GetServer() { return m_Server; };
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest; bool IsV6() const { return m_RemoteEndpoint.address().is_v6(); };
SessionState m_State;
bool m_IsSessionKey; void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs);
uint32_t m_RelayTag; // received from peer
uint32_t m_SentRelayTag; // sent by us void SendPeerTest(); // Alice
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
i2p::crypto::CBCDecryption m_SessionKeyDecryption; SessionState GetState() const { return m_State; };
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey; size_t GetNumSentBytes() const { return m_NumSentBytes; };
i2p::data::RouterInfo::IntroKey m_IntroKey;
SSUData m_Data; size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only void SendKeepAlive();
std::map<uint32_t, std::pair <std::shared_ptr<const i2p::data::RouterInfo>, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp)
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server uint32_t GetRelayTag() const { return m_RelayTag; };
};
} const i2p::data::RouterInfo::IntroKey &GetIntroKey() const { return m_IntroKey; };
void FlushData();
void CleanUp(uint64_t ts);
private:
boost::asio::io_service &GetService();
void CreateAESandMacKey(const uint8_t *pubKey);
size_t GetSSUHeaderSize(const uint8_t *buf) const;
void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage(uint8_t *buf, size_t len,
const boost::asio::ip::udp::endpoint &senderEndpoint); // call for established session
void ProcessSessionRequest(const uint8_t *buf, size_t len);
void SendSessionRequest();
void SendRelayRequest(const i2p::data::RouterInfo::Introducer &introducer, uint32_t nonce);
void ProcessSessionCreated(uint8_t *buf, size_t len);
void SendSessionCreated(const uint8_t *x, bool sendRelayTag = true);
void ProcessSessionConfirmed(const uint8_t *buf, size_t len);
void SendSessionConfirmed(const uint8_t *y, const uint8_t *ourAddress, size_t ourAddressLen);
void ProcessRelayRequest(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &from);
void SendRelayResponse(uint32_t nonce, const boost::asio::ip::udp::endpoint &from,
const uint8_t *introKey, const boost::asio::ip::udp::endpoint &to);
void SendRelayIntro(std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint &from);
void ProcessRelayResponse(const uint8_t *buf, size_t len);
void ProcessRelayIntro(const uint8_t *buf, size_t len);
void Established();
void ScheduleConnectTimer();
void HandleConnectTimer(const boost::system::error_code &ecode);
void ProcessPeerTest(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
void SendPeerTest(uint32_t nonce, const boost::asio::ip::address &address, uint16_t port,
const uint8_t *introKey, bool toAddress = true, bool sendAddress = true);
void ProcessData(uint8_t *buf, size_t len);
void SendSessionDestroyed();
void Send(uint8_t type, const uint8_t *payload, size_t len); // with session key
void Send(const uint8_t *buf, size_t size);
void FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *buf, size_t len, const i2p::crypto::AESKey &aesKey,
const uint8_t *iv, const i2p::crypto::MACKey &macKey, uint8_t flag = 0);
void FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *buf, size_t len); // with session key
void FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *in, size_t len, uint8_t *out); // with session key
void Decrypt(uint8_t *buf, size_t len, const i2p::crypto::AESKey &aesKey);
void DecryptSessionKey(uint8_t *buf, size_t len);
bool Validate(uint8_t *buf, size_t len, const i2p::crypto::MACKey &macKey);
void Reset();
static size_t ExtractIPAddressAndPort(const uint8_t *buf, size_t len, boost::asio::ip::address &ip,
uint16_t &port); // returns actual buf size
private:
friend class SSUData; // TODO: change in later
SSUServer &m_Server;
const boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest;
SessionState m_State;
bool m_IsSessionKey;
uint32_t m_RelayTag; // received from peer
uint32_t m_SentRelayTag; // sent by us
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey;
SSUData m_Data;
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
std::map<uint32_t, std::pair<std::shared_ptr<const i2p::data::RouterInfo>, uint64_t> > m_RelayRequests; // nonce->(Charlie, timestamp)
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
};
}
} }
#endif #endif

View file

@ -10,131 +10,124 @@
#include "Log.h" #include "Log.h"
#include "Signature.h" #include "Signature.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EDDSA25519Verifier::EDDSA25519Verifier () EDDSA25519Verifier::EDDSA25519Verifier ()
{ {
m_MDCtx = EVP_MD_CTX_create (); m_MDCtx = EVP_MD_CTX_create ();
} }
EDDSA25519Verifier::~EDDSA25519Verifier () EDDSA25519Verifier::~EDDSA25519Verifier ()
{ {
EVP_MD_CTX_destroy (m_MDCtx); EVP_MD_CTX_destroy (m_MDCtx);
} }
void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey)
{ {
EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32);
EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey); EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey);
EVP_PKEY_free (pkey); EVP_PKEY_free (pkey);
} }
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{ {
return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len);
} }
#else #else
EDDSA25519Verifier::EDDSA25519Verifier ()
{
}
EDDSA25519Verifier::~EDDSA25519Verifier () EDDSA25519Verifier::EDDSA25519Verifier() {
{ }
}
void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) EDDSA25519Verifier::~EDDSA25519Verifier() {
{ }
memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
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 void EDDSA25519Verifier::SetPublicKey(const uint8_t *signingKey) {
{ memcpy(m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
uint8_t digest[64]; BN_CTX *ctx = BN_CTX_new();
SHA512_CTX ctx; m_PublicKey = GetEd25519()->DecodePublicKey(m_PublicKeyEncoded, ctx);
SHA512_Init (&ctx); BN_CTX_free(ctx);
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R }
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data bool EDDSA25519Verifier::Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
SHA512_Final (digest, &ctx); uint8_t digest[64];
SHA512_CTX ctx;
SHA512_Init(&ctx);
SHA512_Update(&ctx, signature, EDDSA25519_SIGNATURE_LENGTH / 2); // R
SHA512_Update(&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update(&ctx, buf, len); // data
SHA512_Final(digest, &ctx);
return GetEd25519()->Verify(m_PublicKey, digest, signature);
}
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
}
#endif #endif
EDDSA25519SignerCompat::EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) EDDSA25519SignerCompat::EDDSA25519SignerCompat(const uint8_t *signingPrivateKey,
{ const uint8_t *signingPublicKey) {
// expand key // expand key
Ed25519::ExpandPrivateKey (signingPrivateKey, m_ExpandedPrivateKey); Ed25519::ExpandPrivateKey(signingPrivateKey, m_ExpandedPrivateKey);
// generate and encode public key // generate and encode public key
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); auto publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) if (signingPublicKey && memcmp(m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) {
{ // keys don't match, it means older key with 0x1F
// keys don't match, it means older key with 0x1F LogPrint(eLogWarning, "Older EdDSA key detected");
LogPrint (eLogWarning, "Older EdDSA key detected"); m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx);
publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); }
} BN_CTX_free(ctx);
BN_CTX_free (ctx); }
}
EDDSA25519SignerCompat::~EDDSA25519SignerCompat () EDDSA25519SignerCompat::~EDDSA25519SignerCompat() {
{ }
}
void EDDSA25519SignerCompat::Sign (const uint8_t * buf, int len, uint8_t * signature) const void EDDSA25519SignerCompat::Sign(const uint8_t *buf, int len, uint8_t *signature) const {
{ GetEd25519()->Sign(m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
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): EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey):
m_MDCtx (nullptr), m_Fallback (nullptr) m_MDCtx (nullptr), m_Fallback (nullptr)
{ {
EVP_PKEY * pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); EVP_PKEY * 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; size_t len = EDDSA25519_PUBLIC_KEY_LENGTH;
EVP_PKEY_get_raw_public_key (pkey, publicKey, &len); EVP_PKEY_get_raw_public_key (pkey, publicKey, &len);
if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
{ {
LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback");
m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey);
} }
else else
{ {
m_MDCtx = EVP_MD_CTX_create (); m_MDCtx = EVP_MD_CTX_create ();
EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey); EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey);
} }
EVP_PKEY_free (pkey); EVP_PKEY_free (pkey);
} }
EDDSA25519Signer::~EDDSA25519Signer () EDDSA25519Signer::~EDDSA25519Signer ()
{ {
if (m_Fallback) delete m_Fallback; if (m_Fallback) delete m_Fallback;
EVP_MD_CTX_destroy (m_MDCtx); EVP_MD_CTX_destroy (m_MDCtx);
} }
void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{ {
if (m_Fallback) return m_Fallback->Sign (buf, len, signature); if (m_Fallback) return m_Fallback->Sign (buf, len, signature);
else else
{ {
size_t l = 64; size_t l = 64;
uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 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); memcpy (signature, sig, 64);
} }
} }
#endif #endif
} }
} }

View file

@ -19,518 +19,511 @@
#include "Ed25519.h" #include "Ed25519.h"
#include "Gost.h" #include "Gost.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto class Verifier {
{ public:
class Verifier
{
public:
virtual ~Verifier () {}; virtual ~Verifier() {};
virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0;
virtual size_t GetPublicKeyLen () const = 0;
virtual size_t GetSignatureLen () const = 0;
virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; };
virtual void SetPublicKey (const uint8_t * signingKey) = 0;
};
class Signer virtual bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const = 0;
{
public:
virtual ~Signer () {}; virtual size_t GetPublicKeyLen() const = 0;
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
};
const size_t DSA_PUBLIC_KEY_LENGTH = 128; virtual size_t GetSignatureLen() const = 0;
const size_t DSA_SIGNATURE_LENGTH = 40;
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2;
class DSAVerifier: public Verifier
{
public:
DSAVerifier () virtual size_t GetPrivateKeyLen() const { return GetSignatureLen() / 2; };
{
m_PublicKey = CreateDSA ();
}
void SetPublicKey (const uint8_t * signingKey) virtual void SetPublicKey(const uint8_t *signingKey) = 0;
{ };
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
}
~DSAVerifier () class Signer {
{ public:
DSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const virtual ~Signer() {};
{
// calculate SHA1 digest
uint8_t digest[20];
SHA1 (buf, len, digest);
// signature
DSA_SIG * sig = DSA_SIG_new();
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
// DSA verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; virtual void Sign(const uint8_t *buf, int len, uint8_t *signature) const = 0;
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; }; };
private: const size_t DSA_PUBLIC_KEY_LENGTH = 128;
const size_t DSA_SIGNATURE_LENGTH = 40;
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH / 2;
DSA * m_PublicKey; class DSAVerifier : public Verifier {
}; public:
class DSASigner: public Signer DSAVerifier() {
{ m_PublicKey = CreateDSA();
public: }
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) void SetPublicKey(const uint8_t *signingKey) {
// openssl 1.1 always requires DSA public key even for signing DSA_set0_key(m_PublicKey, BN_bin2bn(signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
{ }
m_PrivateKey = CreateDSA ();
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
}
~DSASigner () ~DSAVerifier() {
{ DSA_free(m_PublicKey);
DSA_free (m_PrivateKey); }
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
{ // calculate SHA1 digest
uint8_t digest[20]; uint8_t digest[20];
SHA1 (buf, len, digest); SHA1(buf, len, digest);
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey); // signature
const BIGNUM * r, * s; DSA_SIG *sig = DSA_SIG_new();
DSA_SIG_get0 (sig, &r, &s); DSA_SIG_set0(sig, BN_bin2bn(signature, DSA_SIGNATURE_LENGTH / 2, NULL),
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); BN_bin2bn(signature + DSA_SIGNATURE_LENGTH / 2, DSA_SIGNATURE_LENGTH / 2, NULL));
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); // DSA verification
DSA_SIG_free(sig); int ret = DSA_do_verify(digest, 20, sig, m_PublicKey);
} DSA_SIG_free(sig);
return ret;
}
private: size_t GetPublicKeyLen() const { return DSA_PUBLIC_KEY_LENGTH; };
DSA * m_PrivateKey; size_t GetSignatureLen() const { return DSA_SIGNATURE_LENGTH; };
};
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) private:
{
DSA * dsa = CreateDSA ();
DSA_generate_key (dsa);
const BIGNUM * pub_key, * priv_key;
DSA_get0_key(dsa, &pub_key, &priv_key);
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
}
struct SHA256Hash DSA *m_PublicKey;
{ };
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA256 (buf, len, digest);
}
enum { hashLen = 32 }; class DSASigner : public Signer {
}; public:
struct SHA384Hash DSASigner(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey)
{ // openssl 1.1 always requires DSA public key even for signing
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) {
{ m_PrivateKey = CreateDSA();
SHA384 (buf, len, digest); DSA_set0_key(m_PrivateKey, BN_bin2bn(signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL),
} BN_bin2bn(signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
}
enum { hashLen = 48 }; ~DSASigner() {
}; DSA_free(m_PrivateKey);
}
struct SHA512Hash void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
{ uint8_t digest[20];
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) SHA1(buf, len, digest);
{ DSA_SIG *sig = DSA_do_sign(digest, 20, m_PrivateKey);
SHA512 (buf, len, digest); const BIGNUM *r, *s;
} DSA_SIG_get0(sig, &r, &s);
bn2buf(r, signature, DSA_SIGNATURE_LENGTH / 2);
bn2buf(s, signature + DSA_SIGNATURE_LENGTH / 2, DSA_SIGNATURE_LENGTH / 2);
DSA_SIG_free(sig);
}
enum { hashLen = 64 }; private:
};
// EcDSA DSA *m_PrivateKey;
template<typename Hash, int curve, size_t keyLen> };
class ECDSAVerifier: public Verifier
{
public:
ECDSAVerifier () inline void CreateDSARandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ DSA *dsa = CreateDSA();
m_PublicKey = EC_KEY_new_by_curve_name (curve); DSA_generate_key(dsa);
} const BIGNUM *pub_key, *priv_key;
DSA_get0_key(dsa, &pub_key, &priv_key);
bn2buf(priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf(pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free(dsa);
}
void SetPublicKey (const uint8_t * signingKey) struct SHA256Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL); SHA256(buf, len, digest);
BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL); }
EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y);
BN_free (x); BN_free (y);
}
~ECDSAVerifier () enum {
{ hashLen = 32
EC_KEY_free (m_PublicKey); };
} };
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const struct SHA384Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
uint8_t digest[Hash::hashLen]; SHA384(buf, len, digest);
Hash::CalculateHash (buf, len, digest); }
ECDSA_SIG * sig = ECDSA_SIG_new();
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
ECDSA_SIG_set0(sig, r, s);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen; }; enum {
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length hashLen = 48
};
};
struct SHA512Hash {
static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
SHA512(buf, len, digest);
}
enum {
hashLen = 64
};
};
// EcDSA
template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier : public Verifier {
public:
ECDSAVerifier() {
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);
BN_free(x);
BN_free(y);
}
~ECDSAVerifier() {
EC_KEY_free(m_PublicKey);
}
bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
uint8_t digest[Hash::hashLen];
Hash::CalculateHash(buf, len, digest);
ECDSA_SIG *sig = ECDSA_SIG_new();
auto r = BN_bin2bn(signature, GetSignatureLen() / 2, NULL);
auto s = BN_bin2bn(signature + GetSignatureLen() / 2, GetSignatureLen() / 2, NULL);
ECDSA_SIG_set0(sig, r, s);
// ECDSA verification
int ret = ECDSA_do_verify(digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen() const { return keyLen; };
size_t GetSignatureLen() const { return keyLen; }; // signature length = key length
private: private:
EC_KEY * m_PublicKey; EC_KEY *m_PublicKey;
}; };
template<typename Hash, int curve, size_t keyLen> template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer class ECDSASigner : public Signer {
{ public:
public:
ECDSASigner (const uint8_t * signingPrivateKey) ECDSASigner(const uint8_t *signingPrivateKey) {
{ m_PrivateKey = EC_KEY_new_by_curve_name(curve);
m_PrivateKey = EC_KEY_new_by_curve_name (curve); EC_KEY_set_private_key(m_PrivateKey, BN_bin2bn(signingPrivateKey, keyLen / 2, NULL));
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL)); }
}
~ECDSASigner () ~ECDSASigner() {
{ EC_KEY_free(m_PrivateKey);
EC_KEY_free (m_PrivateKey); }
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
{ uint8_t digest[Hash::hashLen];
uint8_t digest[Hash::hashLen]; Hash::CalculateHash(buf, len, digest);
Hash::CalculateHash (buf, len, digest); ECDSA_SIG *sig = ECDSA_do_sign(digest, Hash::hashLen, m_PrivateKey);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey); const BIGNUM *r, *s;
const BIGNUM * r, * s; ECDSA_SIG_get0(sig, &r, &s);
ECDSA_SIG_get0 (sig, &r, &s); // signatureLen = keyLen
// signatureLen = keyLen bn2buf(r, signature, keyLen / 2);
bn2buf (r, signature, keyLen/2); bn2buf(s, signature + keyLen / 2, keyLen / 2);
bn2buf (s, signature + keyLen/2, keyLen/2); ECDSA_SIG_free(sig);
ECDSA_SIG_free(sig); }
}
private: private:
EC_KEY * m_PrivateKey; EC_KEY *m_PrivateKey;
}; };
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void
{ CreateECDSARandomKeys(int curve, size_t keyLen, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve); EC_KEY *signingKey = EC_KEY_new_by_curve_name(curve);
EC_KEY_generate_key (signingKey); EC_KEY_generate_key(signingKey);
bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2); bn2buf(EC_KEY_get0_private_key(signingKey), signingPrivateKey, keyLen / 2);
BIGNUM * x = BN_new(), * y = BN_new(); BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey), EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(signingKey),
EC_KEY_get0_public_key (signingKey), x, y, NULL); EC_KEY_get0_public_key(signingKey), x, y, NULL);
bn2buf (x, signingPublicKey, keyLen/2); bn2buf(x, signingPublicKey, keyLen / 2);
bn2buf (y, signingPublicKey + keyLen/2, keyLen/2); bn2buf(y, signingPublicKey + keyLen / 2, keyLen / 2);
BN_free (x); BN_free (y); BN_free(x);
EC_KEY_free (signingKey); BN_free(y);
} EC_KEY_free(signingKey);
}
// ECDSA_SHA256_P256 // ECDSA_SHA256_P256
const size_t ECDSAP256_KEY_LENGTH = 64; const size_t ECDSAP256_KEY_LENGTH = 64;
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier; typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer; typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateECDSAP256RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ CreateECDSARandomKeys(NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); }
}
// ECDSA_SHA384_P384 // ECDSA_SHA384_P384
const size_t ECDSAP384_KEY_LENGTH = 96; const size_t ECDSAP384_KEY_LENGTH = 96;
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier; typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer; typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateECDSAP384RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ CreateECDSARandomKeys(NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); }
}
// ECDSA_SHA512_P521 // ECDSA_SHA512_P521
const size_t ECDSAP521_KEY_LENGTH = 132; const size_t ECDSAP521_KEY_LENGTH = 132;
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier; typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer; typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateECDSAP521RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ CreateECDSARandomKeys(NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); }
}
// EdDSA // EdDSA
class EDDSA25519Verifier: public Verifier class EDDSA25519Verifier : public Verifier {
{ public:
public:
EDDSA25519Verifier (); EDDSA25519Verifier();
void SetPublicKey (const uint8_t * signingKey);
~EDDSA25519Verifier ();
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; void SetPublicKey(const uint8_t *signingKey);
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; }; ~EDDSA25519Verifier();
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
private: bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const;
size_t GetPublicKeyLen() const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen() const { return EDDSA25519_SIGNATURE_LENGTH; };
private:
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EVP_MD_CTX * m_MDCtx; EVP_MD_CTX * m_MDCtx;
#else #else
EDDSAPoint m_PublicKey; EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
#endif #endif
}; };
class EDDSA25519SignerCompat: public Signer class EDDSA25519SignerCompat : public Signer {
{ public:
public:
EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); 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; // we pass signingPublicKey to check if it matches private key
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation ~EDDSA25519SignerCompat();
private: void Sign(const uint8_t *buf, int len, uint8_t *signature) const;
uint8_t m_ExpandedPrivateKey[64]; const uint8_t *GetPublicKey() const { return m_PublicKeyEncoded; }; // for keys creation
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
}; private:
uint8_t m_ExpandedPrivateKey[64];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
class EDDSA25519Signer: public Signer class EDDSA25519Signer: public Signer
{ {
public: public:
EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr);
// we pass signingPublicKey to check if it matches private key // we pass signingPublicKey to check if it matches private key
~EDDSA25519Signer (); ~EDDSA25519Signer ();
void Sign (const uint8_t * buf, int len, uint8_t * signature) const; void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
private: private:
EVP_MD_CTX * m_MDCtx; EVP_MD_CTX * m_MDCtx;
EDDSA25519SignerCompat * m_Fallback; EDDSA25519SignerCompat * m_Fallback;
}; };
#else #else
typedef EDDSA25519SignerCompat EDDSA25519Signer; typedef EDDSA25519SignerCompat EDDSA25519Signer;
#endif #endif
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateEDDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EVP_PKEY *pkey = NULL; EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL); EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL);
EVP_PKEY_keygen_init (pctx); EVP_PKEY_keygen_init (pctx);
EVP_PKEY_keygen (pctx, &pkey); EVP_PKEY_keygen (pctx, &pkey);
EVP_PKEY_CTX_free (pctx); EVP_PKEY_CTX_free (pctx);
size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; size_t len = EDDSA25519_PUBLIC_KEY_LENGTH;
EVP_PKEY_get_raw_public_key (pkey, signingPublicKey, &len); EVP_PKEY_get_raw_public_key (pkey, signingPublicKey, &len);
len = EDDSA25519_PRIVATE_KEY_LENGTH; len = EDDSA25519_PRIVATE_KEY_LENGTH;
EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len); EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len);
EVP_PKEY_free (pkey); EVP_PKEY_free (pkey);
#else #else
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); RAND_bytes(signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey); EDDSA25519Signer signer(signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);
#endif #endif
} }
// ГОСТ Р 34.11 // ГОСТ Р 34.11
struct GOSTR3411_256_Hash struct GOSTR3411_256_Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) GOSTR3411_2012_256(buf, len, digest);
{ }
GOSTR3411_2012_256 (buf, len, digest);
}
enum { hashLen = 32 }; enum {
}; hashLen = 32
};
};
struct GOSTR3411_512_Hash struct GOSTR3411_512_Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) GOSTR3411_2012_512(buf, len, digest);
{ }
GOSTR3411_2012_512 (buf, len, digest);
}
enum { hashLen = 64 }; enum {
}; hashLen = 64
};
};
// ГОСТ Р 34.10 // ГОСТ Р 34.10
const size_t GOSTR3410_256_PUBLIC_KEY_LENGTH = 64; const size_t GOSTR3410_256_PUBLIC_KEY_LENGTH = 64;
const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128; const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128;
template<typename Hash> template<typename Hash>
class GOSTR3410Verifier: public Verifier class GOSTR3410Verifier : public Verifier {
{ public:
public:
enum { keyLen = Hash::hashLen }; enum {
keyLen = Hash::hashLen
};
GOSTR3410Verifier (GOSTR3410ParamSet paramSet): GOSTR3410Verifier(GOSTR3410ParamSet paramSet) :
m_ParamSet (paramSet), m_PublicKey (nullptr) 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 * x = BN_bin2bn (signingKey, GetPublicKeyLen ()/2, NULL); BIGNUM *y = BN_bin2bn(signingKey + GetPublicKeyLen() / 2, GetPublicKeyLen() / 2, NULL);
BIGNUM * y = BN_bin2bn (signingKey + GetPublicKeyLen ()/2, GetPublicKeyLen ()/2, NULL); m_PublicKey = GetGOSTR3410Curve(m_ParamSet)->CreatePoint(x, y);
m_PublicKey = GetGOSTR3410Curve (m_ParamSet)->CreatePoint (x, y); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
} }
~GOSTR3410Verifier ()
{
if (m_PublicKey) EC_POINT_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const ~GOSTR3410Verifier() {
{ if (m_PublicKey) EC_POINT_free(m_PublicKey);
uint8_t digest[Hash::hashLen]; }
Hash::CalculateHash (buf, len, digest);
BIGNUM * d = BN_bin2bn (digest, Hash::hashLen, nullptr);
BIGNUM * r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
BIGNUM * s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
bool ret = GetGOSTR3410Curve (m_ParamSet)->Verify (m_PublicKey, d, r, s);
BN_free (d); BN_free (r); BN_free (s);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen*2; } bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
size_t GetSignatureLen () const { return keyLen*2; } uint8_t digest[Hash::hashLen];
Hash::CalculateHash(buf, len, digest);
BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
BIGNUM *r = BN_bin2bn(signature, GetSignatureLen() / 2, NULL);
BIGNUM *s = BN_bin2bn(signature + GetSignatureLen() / 2, GetSignatureLen() / 2, NULL);
bool ret = GetGOSTR3410Curve(m_ParamSet)->Verify(m_PublicKey, d, r, s);
BN_free(d);
BN_free(r);
BN_free(s);
return ret;
}
private: size_t GetPublicKeyLen() const { return keyLen * 2; }
GOSTR3410ParamSet m_ParamSet; size_t GetSignatureLen() const { return keyLen * 2; }
EC_POINT * m_PublicKey;
};
template<typename Hash> private:
class GOSTR3410Signer: public Signer
{
public:
enum { keyLen = Hash::hashLen }; GOSTR3410ParamSet m_ParamSet;
EC_POINT *m_PublicKey;
};
GOSTR3410Signer (GOSTR3410ParamSet paramSet, const uint8_t * signingPrivateKey): template<typename Hash>
m_ParamSet (paramSet) class GOSTR3410Signer : public Signer {
{ public:
m_PrivateKey = BN_bin2bn (signingPrivateKey, keyLen, nullptr);
}
~GOSTR3410Signer () { BN_free (m_PrivateKey); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const enum {
{ keyLen = Hash::hashLen
uint8_t digest[Hash::hashLen]; };
Hash::CalculateHash (buf, len, digest);
BIGNUM * d = BN_bin2bn (digest, Hash::hashLen, nullptr);
BIGNUM * r = BN_new (), * s = BN_new ();
GetGOSTR3410Curve (m_ParamSet)->Sign (m_PrivateKey, d, r, s);
bn2buf (r, signature, keyLen);
bn2buf (s, signature + keyLen, keyLen);
BN_free (d); BN_free (r); BN_free (s);
}
private: GOSTR3410Signer(GOSTR3410ParamSet paramSet, const uint8_t *signingPrivateKey) :
m_ParamSet(paramSet) {
m_PrivateKey = BN_bin2bn(signingPrivateKey, keyLen, nullptr);
}
GOSTR3410ParamSet m_ParamSet; ~GOSTR3410Signer() { BN_free(m_PrivateKey); }
BIGNUM * m_PrivateKey;
};
inline void CreateGOSTR3410RandomKeys (GOSTR3410ParamSet paramSet, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
{ uint8_t digest[Hash::hashLen];
const auto& curve = GetGOSTR3410Curve (paramSet); Hash::CalculateHash(buf, len, digest);
auto keyLen = curve->GetKeyLen (); BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
RAND_bytes (signingPrivateKey, keyLen); BIGNUM *r = BN_new(), *s = BN_new();
BIGNUM * priv = BN_bin2bn (signingPrivateKey, keyLen, nullptr); GetGOSTR3410Curve(m_ParamSet)->Sign(m_PrivateKey, d, r, s);
bn2buf(r, signature, keyLen);
bn2buf(s, signature + keyLen, keyLen);
BN_free(d);
BN_free(r);
BN_free(s);
}
auto pub = curve->MulP (priv); private:
BN_free (priv);
BIGNUM * x = BN_new (), * y = BN_new ();
curve->GetXY (pub, x, y);
EC_POINT_free (pub);
bn2buf (x, signingPublicKey, keyLen);
bn2buf (y, signingPublicKey + keyLen, keyLen);
BN_free (x); BN_free (y);
}
typedef GOSTR3410Verifier<GOSTR3411_256_Hash> GOSTR3410_256_Verifier; GOSTR3410ParamSet m_ParamSet;
typedef GOSTR3410Signer<GOSTR3411_256_Hash> GOSTR3410_256_Signer; BIGNUM *m_PrivateKey;
typedef GOSTR3410Verifier<GOSTR3411_512_Hash> GOSTR3410_512_Verifier; };
typedef GOSTR3410Signer<GOSTR3411_512_Hash> GOSTR3410_512_Signer;
// RedDSA inline void
typedef EDDSA25519Verifier RedDSA25519Verifier; CreateGOSTR3410RandomKeys(GOSTR3410ParamSet paramSet, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
class RedDSA25519Signer: public Signer const auto &curve = GetGOSTR3410Curve(paramSet);
{ auto keyLen = curve->GetKeyLen();
public: RAND_bytes(signingPrivateKey, keyLen);
BIGNUM *priv = BN_bin2bn(signingPrivateKey, keyLen, nullptr);
RedDSA25519Signer (const uint8_t * signingPrivateKey) auto pub = curve->MulP(priv);
{ BN_free(priv);
memcpy (m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); BIGNUM *x = BN_new(), *y = BN_new();
BN_CTX * ctx = BN_CTX_new (); curve->GetXY(pub, x, y);
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_PrivateKey, ctx); EC_POINT_free(pub);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); bn2buf(x, signingPublicKey, keyLen);
BN_CTX_free (ctx); bn2buf(y, signingPublicKey + keyLen, keyLen);
} BN_free(x);
~RedDSA25519Signer () {}; BN_free(y);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const typedef GOSTR3410Verifier<GOSTR3411_256_Hash> GOSTR3410_256_Verifier;
{ typedef GOSTR3410Signer<GOSTR3411_256_Hash> GOSTR3410_256_Signer;
GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature); typedef GOSTR3410Verifier<GOSTR3411_512_Hash> GOSTR3410_512_Verifier;
} typedef GOSTR3410Signer<GOSTR3411_512_Hash> GOSTR3410_512_Signer;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation // RedDSA
typedef EDDSA25519Verifier RedDSA25519Verifier;
private: class RedDSA25519Signer : public Signer {
public:
uint8_t m_PrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH]; RedDSA25519Signer(const uint8_t *signingPrivateKey) {
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; memcpy(m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
}; BN_CTX *ctx = BN_CTX_new();
auto publicKey = GetEd25519()->GeneratePublicKey(m_PrivateKey, ctx);
GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
BN_CTX_free(ctx);
}
inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) ~RedDSA25519Signer() {};
{
GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey); void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
RedDSA25519Signer signer (signingPrivateKey); GetEd25519()->SignRedDSA(m_PrivateKey, m_PublicKeyEncoded, buf, len, signature);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); }
}
} 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];
};
inline void CreateRedDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
GetEd25519()->CreateRedDSAPrivateKey(signingPrivateKey);
RedDSA25519Signer signer(signingPrivateKey);
memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);
}
}
} }
#endif #endif

View file

@ -12,143 +12,131 @@
#include "Crypto.h" #include "Crypto.h"
#if !OPENSSL_SIPHASH #if !OPENSSL_SIPHASH
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto namespace siphash {
{ constexpr int crounds = 2;
namespace siphash constexpr int drounds = 4;
{
constexpr int crounds = 2;
constexpr int drounds = 4;
inline uint64_t rotl(const uint64_t & x, int b) inline uint64_t rotl(const uint64_t &x, int b) {
{ uint64_t ret = x << b;
uint64_t ret = x << b; ret |= x >> (64 - b);
ret |= x >> (64 - b); return ret;
return ret; }
}
inline void u32to8le(const uint32_t & v, uint8_t * p) inline void u32to8le(const uint32_t &v, uint8_t *p) {
{ p[0] = (uint8_t) v;
p[0] = (uint8_t) v; p[1] = (uint8_t)(v >> 8);
p[1] = (uint8_t) (v >> 8); p[2] = (uint8_t)(v >> 16);
p[2] = (uint8_t) (v >> 16); p[3] = (uint8_t)(v >> 24);
p[3] = (uint8_t) (v >> 24); }
}
inline void u64to8le(const uint64_t & v, uint8_t * p) inline void u64to8le(const uint64_t &v, uint8_t *p) {
{ p[0] = v & 0xff;
p[0] = v & 0xff; p[1] = (v >> 8) & 0xff;
p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff;
p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff;
p[3] = (v >> 24) & 0xff; p[4] = (v >> 32) & 0xff;
p[4] = (v >> 32) & 0xff; p[5] = (v >> 40) & 0xff;
p[5] = (v >> 40) & 0xff; p[6] = (v >> 48) & 0xff;
p[6] = (v >> 48) & 0xff; p[7] = (v >> 56) & 0xff;
p[7] = (v >> 56) & 0xff; }
}
inline uint64_t u8to64le(const uint8_t * p) inline uint64_t u8to64le(const uint8_t *p) {
{ uint64_t i = 0;
uint64_t i = 0; int idx = 0;
int idx = 0; while (idx < 8) {
while(idx < 8) i |= ((uint64_t) p[idx]) << (idx * 8);
{ ++idx;
i |= ((uint64_t) p[idx]) << (idx * 8); }
++idx; return i;
} }
return i;
}
inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) inline void round(uint64_t &_v0, uint64_t &_v1, uint64_t &_v2, uint64_t &_v3) {
{ _v0 += _v1;
_v0 += _v1; _v1 = rotl(_v1, 13);
_v1 = rotl(_v1, 13); _v1 ^= _v0;
_v1 ^= _v0; _v0 = rotl(_v0, 32);
_v0 = rotl(_v0, 32); _v2 += _v3;
_v2 += _v3; _v3 = rotl(_v3, 16);
_v3 = rotl(_v3, 16); _v3 ^= _v2;
_v3 ^= _v2; _v0 += _v3;
_v0 += _v3; _v3 = rotl(_v3, 21);
_v3 = rotl(_v3, 21); _v3 ^= _v0;
_v3 ^= _v0; _v2 += _v1;
_v2 += _v1; _v1 = rotl(_v1, 17);
_v1 = rotl(_v1, 17); _v1 ^= _v2;
_v1 ^= _v2; _v2 = rotl(_v2, 32);
_v2 = rotl(_v2, 32); }
} }
}
/** hashsz must be 8 or 16 */ /** hashsz must be 8 or 16 */
template<std::size_t hashsz> template<std::size_t hashsz>
inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) inline void Siphash(uint8_t *h, const uint8_t *buf, std::size_t bufsz, const uint8_t *key) {
{ uint64_t v0 = 0x736f6d6570736575ULL;
uint64_t v0 = 0x736f6d6570736575ULL; uint64_t v1 = 0x646f72616e646f6dULL;
uint64_t v1 = 0x646f72616e646f6dULL; uint64_t v2 = 0x6c7967656e657261ULL;
uint64_t v2 = 0x6c7967656e657261ULL; uint64_t v3 = 0x7465646279746573ULL;
uint64_t v3 = 0x7465646279746573ULL; const uint64_t k0 = siphash::u8to64le(key);
const uint64_t k0 = siphash::u8to64le(key); const uint64_t k1 = siphash::u8to64le(key + 8);
const uint64_t k1 = siphash::u8to64le(key + 8); uint64_t msg;
uint64_t msg; int i;
int i; const uint8_t *end = buf + bufsz - (bufsz % sizeof(uint64_t));
const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); auto left = bufsz & 7;
auto left = bufsz & 7; uint64_t b = ((uint64_t) bufsz) << 56;
uint64_t b = ((uint64_t)bufsz) << 56; v3 ^= k1;
v3 ^= k1; v2 ^= k0;
v2 ^= k0; v1 ^= k1;
v1 ^= k1; v0 ^= k0;
v0 ^= k0;
if(hashsz == 16) v1 ^= 0xee; if (hashsz == 16) v1 ^= 0xee;
while(buf != end) while (buf != end) {
{ msg = siphash::u8to64le(buf);
msg = siphash::u8to64le(buf); v3 ^= msg;
v3 ^= msg; for (i = 0; i < siphash::crounds; ++i)
for(i = 0; i < siphash::crounds; ++i) siphash::round(v0, v1, v2, v3);
siphash::round(v0, v1, v2, v3);
v0 ^= msg; v0 ^= msg;
buf += 8; buf += 8;
} }
while(left) while (left) {
{ --left;
--left; b |= ((uint64_t)(buf[left])) << (left * 8);
b |= ((uint64_t)(buf[left])) << (left * 8); }
}
v3 ^= b; v3 ^= b;
for(i = 0; i < siphash::crounds; ++i) for (i = 0; i < siphash::crounds; ++i)
siphash::round(v0, v1, v2, v3); siphash::round(v0, v1, v2, v3);
v0 ^= b; v0 ^= b;
if(hashsz == 16) if (hashsz == 16)
v2 ^= 0xee; v2 ^= 0xee;
else else
v2 ^= 0xff; v2 ^= 0xff;
for(i = 0; i < siphash::drounds; ++i) for (i = 0; i < siphash::drounds; ++i)
siphash::round(v0, v1, v2, v3); 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) for (i = 0; i < siphash::drounds; ++i)
siphash::round(v0, v1, v2, v3); siphash::round(v0, v1, v2, v3);
b = v0 ^ v1 ^ v2 ^ v3; b = v0 ^ v1 ^ v2 ^ v3;
siphash::u64to8le(b, h + 8); siphash::u64to8le(b, h + 8);
} }
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -27,353 +27,419 @@
#include "Tunnel.h" #include "Tunnel.h"
#include "util.h" // MemoryPool #include "util.h" // MemoryPool
namespace i2p namespace i2p {
{ namespace client {
namespace client class ClientDestination;
{ }
class ClientDestination; namespace stream {
} const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001;
namespace stream const uint16_t PACKET_FLAG_CLOSE = 0x0002;
{ const uint16_t PACKET_FLAG_RESET = 0x0004;
const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008;
const uint16_t PACKET_FLAG_CLOSE = 0x0002; const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010;
const uint16_t PACKET_FLAG_RESET = 0x0004; const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020;
const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040;
const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080;
const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100;
const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; const uint16_t PACKET_FLAG_ECHO = 0x0200;
const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800;
const uint16_t PACKET_FLAG_ECHO = 0x0200;
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800;
const size_t STREAMING_MTU = 1730; const size_t STREAMING_MTU = 1730;
const size_t STREAMING_MTU_RATCHETS = 1812; const size_t STREAMING_MTU_RATCHETS = 1812;
const size_t MAX_PACKET_SIZE = 4096; const size_t MAX_PACKET_SIZE = 4096;
const size_t COMPRESSION_THRESHOLD_SIZE = 66; const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int MAX_NUM_RESEND_ATTEMPTS = 6; const int MAX_NUM_RESEND_ATTEMPTS = 6;
const int WINDOW_SIZE = 6; // in messages const int WINDOW_SIZE = 6; // in messages
const int MIN_WINDOW_SIZE = 1; const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 128; const int MAX_WINDOW_SIZE = 128;
const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds
const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds
const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128; const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
struct Packet struct Packet {
{ size_t len, offset;
size_t len, offset; uint8_t buf[MAX_PACKET_SIZE];
uint8_t buf[MAX_PACKET_SIZE]; uint64_t sendTime;
uint64_t sendTime;
Packet (): len (0), offset (0), sendTime (0) {}; Packet() : len(0), offset(0), sendTime(0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; uint8_t *GetBuffer() { return buf + offset; };
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
uint32_t GetSeqn () const { return bufbe32toh (buf + 8); };
uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); };
uint8_t GetNACKCount () const { return buf[16]; };
uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); };
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); };
uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; size_t GetLength() const { return len - offset; };
bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; };
bool IsEcho () const { return GetFlags () & PACKET_FLAG_ECHO; };
};
struct PacketCmp uint32_t GetSendStreamID() const { return bufbe32toh(buf); };
{
bool operator() (const Packet * p1, const Packet * p2) const
{
return p1->GetSeqn () < p2->GetSeqn ();
};
};
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler; uint32_t GetReceiveStreamID() const { return bufbe32toh(buf + 4); };
struct SendBuffer
{
uint8_t * buf;
size_t len, offset;
SendHandler handler;
SendBuffer (const uint8_t * b, size_t l, SendHandler h): uint32_t GetSeqn() const { return bufbe32toh(buf + 8); };
len(l), offset (0), handler(h)
{
buf = new uint8_t[len];
memcpy (buf, b, len);
}
SendBuffer (size_t l): // create empty buffer
len(l), offset (0)
{
buf = new uint8_t[len];
}
~SendBuffer ()
{
delete[] buf;
if (handler) handler(boost::system::error_code ());
}
size_t GetRemainingSize () const { return len - offset; };
const uint8_t * GetRemaningBuffer () const { return buf + offset; };
void Cancel () { if (handler) handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); handler = nullptr; };
};
class SendBufferQueue uint32_t GetAckThrough() const { return bufbe32toh(buf + 12); };
{
public:
SendBufferQueue (): m_Size (0) {}; uint8_t GetNACKCount() const { return buf[16]; };
~SendBufferQueue () { CleanUp (); };
void Add (const uint8_t * buf, size_t len, SendHandler handler); uint32_t GetNACK(int i) const { return bufbe32toh(buf + 17 + 4 * i); };
void Add (std::shared_ptr<SendBuffer> buf);
size_t Get (uint8_t * buf, size_t len);
size_t GetSize () const { return m_Size; };
bool IsEmpty () const { return m_Buffers.empty (); };
void CleanUp ();
private: const uint8_t *GetOption() const { return buf + 17 + GetNACKCount() * 4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags() const { return bufbe16toh(GetOption() - 2); };
std::list<std::shared_ptr<SendBuffer> > m_Buffers; uint16_t GetOptionSize() const { return bufbe16toh(GetOption()); };
size_t m_Size;
};
enum StreamStatus const uint8_t *GetOptionData() const { return GetOption() + 2; };
{
eStreamStatusNew = 0,
eStreamStatusOpen,
eStreamStatusReset,
eStreamStatusClosing,
eStreamStatusClosed,
eStreamStatusTerminated
};
class StreamingDestination; const uint8_t *GetPayload() const { return GetOptionData() + GetOptionSize(); };
class Stream: public std::enable_shared_from_this<Stream>
{
public:
Stream (boost::asio::io_service& service, StreamingDestination& local, bool IsSYN() const { return GetFlags() & PACKET_FLAG_SYNCHRONIZE; };
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
~Stream (); bool IsNoAck() const { return GetFlags() & PACKET_FLAG_NO_ACK; };
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet); bool IsEcho() const { return GetFlags() & PACKET_FLAG_ECHO; };
void HandlePing (Packet * packet); };
size_t Send (const uint8_t * buf, size_t len);
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
void SendPing ();
template<typename Buffer, typename ReceiveHandler> struct PacketCmp {
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); bool operator()(const Packet *p1, const Packet *p2) const {
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; return p1->GetSeqn() < p2->GetSeqn();
};
};
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; typedef std::function<void(const boost::system::error_code &ecode)> SendHandler;
/** only call close from destination thread, use Stream::AsyncClose for other threads */ struct SendBuffer {
void Close (); uint8_t *buf;
void Cancel () { m_ReceiveTimer.cancel (); }; size_t len, offset;
SendHandler handler;
size_t GetNumSentBytes () const { return m_NumSentBytes; }; SendBuffer(const uint8_t *b, size_t l, SendHandler h) :
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; len(l), offset(0), handler(h) {
size_t GetSendQueueSize () const { return m_SentPackets.size (); }; buf = new uint8_t[len];
size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); }; memcpy(buf, b, len);
size_t GetSendBufferSize () const { return m_SendBuffer.GetSize (); }; }
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
void Terminate (bool deleteFromDestination = true); SendBuffer(size_t l) : // create empty buffer
len(l), offset(0) {
buf = new uint8_t[len];
}
private: ~SendBuffer() {
delete[] buf;
if (handler) handler(boost::system::error_code());
}
void CleanUp (); size_t GetRemainingSize() const { return len - offset; };
void SendBuffer (); const uint8_t *GetRemaningBuffer() const { return buf + offset; };
void SendQuickAck ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
void SendUpdatedLeaseSet ();
void SavePacket (Packet * packet); void Cancel() {
void ProcessPacket (Packet * packet); if (handler)
bool ProcessOptions (uint16_t flags, Packet * packet); handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted));
void ProcessAck (Packet * packet); handler = nullptr;
size_t ConcatenatePackets (uint8_t * buf, size_t len); };
};
void UpdateCurrentRemoteLease (bool expired = false); class SendBufferQueue {
public:
template<typename Buffer, typename ReceiveHandler> SendBufferQueue() : m_Size(0) {};
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
void ScheduleResend (); ~SendBufferQueue() { CleanUp(); };
void HandleResendTimer (const boost::system::error_code& ecode);
void HandleAckSendTimer (const boost::system::error_code& ecode);
private: void Add(const uint8_t *buf, size_t len, SendHandler handler);
boost::asio::io_service& m_Service; void Add(std::shared_ptr<SendBuffer> buf);
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber;
StreamStatus m_Status;
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port;
std::mutex m_SendBufferMutex; size_t Get(uint8_t *buf, size_t len);
SendBufferQueue m_SendBuffer;
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<StreamingDestination> size_t GetSize() const { return m_Size; };
{
public:
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor; bool IsEmpty() const { return m_Buffers.empty(); };
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = false); void CleanUp();
~StreamingDestination ();
void Start (); private:
void Stop ();
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); std::list<std::shared_ptr<SendBuffer> > m_Buffers;
void SendPing (std::shared_ptr<const i2p::data::LeaseSet> remote); size_t m_Size;
void DeleteStream (std::shared_ptr<Stream> stream); };
bool DeleteStream (uint32_t recvStreamID);
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
void AcceptOnce (const Acceptor& acceptor);
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; }; enum StreamStatus {
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; }; eStreamStatusNew = 0,
uint16_t GetLocalPort () const { return m_LocalPort; }; eStreamStatusOpen,
eStreamStatusReset,
eStreamStatusClosing,
eStreamStatusClosed,
eStreamStatusTerminated
};
void HandleDataMessagePayload (const uint8_t * buf, size_t len); class StreamingDestination;
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true);
Packet * NewPacket () { return m_PacketsPool.Acquire(); } class Stream : public std::enable_shared_from_this<Stream> {
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } public:
private: Stream(boost::asio::io_service &service, StreamingDestination &local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream(boost::asio::io_service &service, StreamingDestination &local); // incoming
void HandleNextPacket (Packet * packet); ~Stream();
std::shared_ptr<Stream> CreateNewIncomingStream (uint32_t receiveStreamID);
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
private: uint32_t GetSendStreamID() const { return m_SendStreamID; };
std::shared_ptr<i2p::client::ClientDestination> m_Owner; uint32_t GetRecvStreamID() const { return m_RecvStreamID; };
uint16_t m_LocalPort;
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
std::shared_ptr<Stream> m_LastStream;
Acceptor m_Acceptor;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::unordered_map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
i2p::util::MemoryPool<Packet> m_PacketsPool; std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet() const { return m_RemoteLeaseSet; };
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
public: std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity() const { return m_RemoteIdentity; };
i2p::data::GzipInflator m_Inflator; bool IsOpen() const { return m_Status == eStreamStatusOpen; };
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
// for HTTP only bool IsEstablished() const { return m_SendStreamID; };
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
}; StreamStatus GetStatus() const { return m_Status; };
StreamingDestination &GetLocalDestination() { return m_LocalDestination; };
void HandleNextPacket(Packet *packet);
void HandlePing(Packet *packet);
size_t Send(const uint8_t *buf, size_t len);
void AsyncSend(const uint8_t *buf, size_t len, SendHandler handler);
void SendPing();
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive(const Buffer &buffer, ReceiveHandler handler, int timeout = 0);
size_t ReadSome(uint8_t *buf, size_t len) { return ConcatenatePackets(buf, len); };
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
/** only call close from destination thread, use Stream::AsyncClose for other threads */
void Close();
void Cancel() { m_ReceiveTimer.cancel(); };
size_t GetNumSentBytes() const { return m_NumSentBytes; };
size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
size_t GetSendQueueSize() const { return m_SentPackets.size(); };
size_t GetReceiveQueueSize() const { return m_ReceiveQueue.size(); };
size_t GetSendBufferSize() const { return m_SendBuffer.GetSize(); };
int GetWindowSize() const { return m_WindowSize; };
int GetRTT() const { return m_RTT; };
void Terminate(bool deleteFromDestination = true);
private:
void CleanUp();
void SendBuffer();
void SendQuickAck();
void SendClose();
bool SendPacket(Packet *packet);
void SendPackets(const std::vector<Packet *> &packets);
void SendUpdatedLeaseSet();
void SavePacket(Packet *packet);
void ProcessPacket(Packet *packet);
bool ProcessOptions(uint16_t flags, Packet *packet);
void ProcessAck(Packet *packet);
size_t ConcatenatePackets(uint8_t *buf, size_t len);
void UpdateCurrentRemoteLease(bool expired = false);
template<typename Buffer, typename ReceiveHandler>
void
HandleReceiveTimer(const boost::system::error_code &ecode, const Buffer &buffer, ReceiveHandler handler,
int remainingTimeout);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
void HandleAckSendTimer(const boost::system::error_code &ecode);
private:
boost::asio::io_service &m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber;
StreamStatus m_Status;
bool m_IsAckSendScheduled;
StreamingDestination &m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port;
std::mutex m_SendBufferMutex;
SendBufferQueue m_SendBuffer;
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<StreamingDestination> {
public:
typedef std::function<void(std::shared_ptr<Stream>)> Acceptor;
StreamingDestination(std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0,
bool gzip = false);
~StreamingDestination();
void Start();
void Stop();
std::shared_ptr<Stream>
CreateNewOutgoingStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing(std::shared_ptr<const i2p::data::LeaseSet> remote);
void DeleteStream(std::shared_ptr<Stream> stream);
bool DeleteStream(uint32_t recvStreamID);
void SetAcceptor(const Acceptor &acceptor);
void ResetAcceptor();
bool IsAcceptorSet() const { return m_Acceptor != nullptr; };
void AcceptOnce(const Acceptor &acceptor);
void AcceptOnceAcceptor(std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
std::shared_ptr<i2p::client::ClientDestination> GetOwner() const { return m_Owner; };
void SetOwner(std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
uint16_t GetLocalPort() const { return m_LocalPort; };
void HandleDataMessagePayload(const uint8_t *buf, size_t len);
std::shared_ptr<I2NPMessage>
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); }
private:
void HandleNextPacket(Packet *packet);
std::shared_ptr<Stream> CreateNewIncomingStream(uint32_t receiveStreamID);
void HandlePendingIncomingTimer(const boost::system::error_code &ecode);
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
uint16_t m_LocalPort;
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
std::shared_ptr<Stream> m_LastStream;
Acceptor m_Acceptor;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::unordered_map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
i2p::util::MemoryPool<Packet> m_PacketsPool;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
public:
i2p::data::GzipInflator m_Inflator;
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
// for HTTP only
const decltype(m_Streams)
&
GetStreams() const { return m_Streams; };
};
//------------------------------------------------- //-------------------------------------------------
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) void Stream::AsyncReceive(const Buffer &buffer, ReceiveHandler handler, int timeout) {
{ auto s = shared_from_this();
auto s = shared_from_this(); m_Service.post([s, buffer, handler, timeout](void) {
m_Service.post ([s, buffer, handler, timeout](void) if (!s->m_ReceiveQueue.empty() || s->m_Status == eStreamStatusReset)
{ s->HandleReceiveTimer(boost::asio::error::make_error_code(boost::asio::error::operation_aborted),
if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset) buffer, handler, 0);
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0); else {
else int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
{ s->m_ReceiveTimer.expires_from_now(boost::posix_time::seconds(t));
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout; int left = timeout - t;
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t)); auto self = s->shared_from_this();
int left = timeout - t; self->m_ReceiveTimer.async_wait(
auto self = s->shared_from_this(); [self, buffer, handler, left](const boost::system::error_code &ec) {
self->m_ReceiveTimer.async_wait ( self->HandleReceiveTimer(ec, buffer, handler, left);
[self, buffer, handler, left](const boost::system::error_code & ec) });
{ }
self->HandleReceiveTimer(ec, buffer, handler, left); });
}); }
}
});
}
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout) void
{ Stream::HandleReceiveTimer(const boost::system::error_code &ecode, const Buffer &buffer, ReceiveHandler handler,
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer)); int remainingTimeout) {
if (received > 0) size_t received = ConcatenatePackets(boost::asio::buffer_cast<uint8_t *>(buffer),
handler (boost::system::error_code (), received); boost::asio::buffer_size(buffer));
else if (ecode == boost::asio::error::operation_aborted) if (received > 0)
{ handler(boost::system::error_code(), received);
// timeout not expired else if (ecode == boost::asio::error::operation_aborted) {
if (m_Status == eStreamStatusReset) // timeout not expired
handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0); if (m_Status == eStreamStatusReset)
else handler(boost::asio::error::make_error_code(boost::asio::error::connection_reset), 0);
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0); else
} handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted), 0);
else } else {
{ // timeout expired
// timeout expired if (remainingTimeout <= 0)
if (remainingTimeout <= 0) handler(boost::asio::error::make_error_code(boost::asio::error::timed_out), received);
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); else {
else // itermediate interrupt
{ SendUpdatedLeaseSet(); // send our leaseset if applicable
// itermediate interrupt AsyncReceive(buffer, handler, remainingTimeout);
SendUpdatedLeaseSet (); // send our leaseset if applicable }
AsyncReceive (buffer, handler, remainingTimeout); }
} }
} }
}
}
} }
#endif #endif

View file

@ -15,92 +15,89 @@
#include "Base.h" #include "Base.h"
namespace i2p { namespace i2p {
namespace data { namespace data {
template<size_t sz> template<size_t sz>
class Tag class Tag {
{ BOOST_STATIC_ASSERT_MSG(sz
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes"); % 8 == 0, "Tag size must be multiple of 8 bytes");
public: public:
Tag () = default; 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); } Tag(const uint8_t *buf) { memcpy(m_Buf, 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; } bool operator==(const Tag &other) const { return !memcmp(m_Buf, other.m_Buf, sz); }
const uint8_t * operator()() const { return m_Buf; }
operator uint8_t * () { return m_Buf; } bool operator!=(const Tag &other) const { return !(*this == other); }
operator const uint8_t * () const { return m_Buf; }
const uint8_t * data() const { return m_Buf; } bool operator<(const Tag &other) const { return memcmp(m_Buf, other.m_Buf, sz) < 0; }
const uint64_t * GetLL () const { return ll; }
bool IsZero () const uint8_t *operator()() { return m_Buf; }
{
for (size_t i = 0; i < sz/8; ++i)
if (ll[i]) return false;
return true;
}
void Fill(uint8_t c) const uint8_t *operator()() const { return m_Buf; }
{
memset(m_Buf, c, sz);
}
void Randomize() operator uint8_t *() { return m_Buf; }
{
RAND_bytes(m_Buf, sz);
}
std::string ToBase64 (size_t len = sz) const operator const uint8_t *() const { return m_Buf; }
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2);
return std::string (str, str + l);
}
std::string ToBase32 (size_t len = sz) const const uint8_t *data() const { return m_Buf; }
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, len, str, sz*2);
return std::string (str, str + l);
}
size_t FromBase32 (const std::string& s) const uint64_t *GetLL() const { return ll; }
{
return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
size_t FromBase64 (const std::string& s) bool IsZero() const {
{ for (size_t i = 0; i < sz / 8; ++i)
return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); if (ll[i]) return false;
} return true;
}
private: void Fill(uint8_t c) {
memset(m_Buf, c, sz);
}
union // 8 bytes aligned void Randomize() {
{ RAND_bytes(m_Buf, sz);
uint8_t m_Buf[sz]; }
uint64_t ll[sz/8];
}; std::string ToBase64(size_t len = sz) const {
}; char str[sz * 2];
} // data size_t l = i2p::data::ByteStreamToBase64(m_Buf, len, str, sz * 2);
return std::string(str, str + l);
}
std::string ToBase32(size_t len = sz) const {
char str[sz * 2];
size_t l = i2p::data::ByteStreamToBase32(m_Buf, len, 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 } // i2p
namespace std namespace std {
{ // hash for std::unordered_map
// hash for std::unordered_map template<size_t sz>
template<size_t sz> struct hash<i2p::data::Tag<sz> > struct hash<i2p::data::Tag<sz> > {
{ size_t operator()(const i2p::data::Tag<sz> &s) const {
size_t operator()(const i2p::data::Tag<sz>& s) const return s.GetLL()[0];
{ }
return s.GetLL ()[0]; };
}
};
} }
#endif /* TAG_H__ */ #endif /* TAG_H__ */

View file

@ -22,238 +22,196 @@
#include "util.h" #include "util.h"
#ifdef _WIN32 #ifdef _WIN32
#ifndef _WIN64 #ifndef _WIN64
#define _USE_32BIT_TIME_T #define _USE_32BIT_TIME_T
#endif #endif
#endif #endif
namespace i2p namespace i2p {
{ namespace util {
namespace util static uint64_t GetLocalMillisecondsSinceEpoch() {
{ return std::chrono::duration_cast<std::chrono::milliseconds>(
static uint64_t GetLocalMillisecondsSinceEpoch () std::chrono::system_clock::now().time_since_epoch()).count();
{ }
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count ();
}
static uint64_t GetLocalSecondsSinceEpoch () static uint64_t GetLocalSecondsSinceEpoch() {
{ return std::chrono::duration_cast<std::chrono::seconds>(
return std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()).count();
std::chrono::system_clock::now().time_since_epoch()).count (); }
}
static uint32_t GetLocalMinutesSinceEpoch () static uint32_t GetLocalMinutesSinceEpoch() {
{ return std::chrono::duration_cast<std::chrono::minutes>(
return std::chrono::duration_cast<std::chrono::minutes>( std::chrono::system_clock::now().time_since_epoch()).count();
std::chrono::system_clock::now().time_since_epoch()).count (); }
}
static uint32_t GetLocalHoursSinceEpoch () static uint32_t GetLocalHoursSinceEpoch() {
{ return std::chrono::duration_cast<std::chrono::hours>(
return std::chrono::duration_cast<std::chrono::hours>( 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 int64_t g_TimeOffset = 0; // in seconds
static void SyncTimeWithNTP (const std::string& address) 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::io_service service; boost::system::error_code ec;
boost::system::error_code ec; auto it = boost::asio::ip::udp::resolver(service).resolve(
auto it = boost::asio::ip::udp::resolver (service).resolve ( boost::asio::ip::udp::resolver::query(address, "ntp"), ec);
boost::asio::ip::udp::resolver::query (address, "ntp"), ec); if (!ec) {
if (!ec) bool found = false;
{ boost::asio::ip::udp::resolver::iterator end;
bool found = false; boost::asio::ip::udp::endpoint ep;
boost::asio::ip::udp::resolver::iterator end; while (it != end) {
boost::asio::ip::udp::endpoint ep; ep = *it;
while (it != end) if (!ep.address().is_unspecified()) {
{ if (ep.address().is_v4()) {
ep = *it; if (i2p::context.SupportsV4()) found = true;
if (!ep.address ().is_unspecified ()) } else if (ep.address().is_v6()) {
{ if (i2p::util::net::IsYggdrasilAddress(ep.address())) {
if (ep.address ().is_v4 ()) if (i2p::context.SupportsMesh()) found = true;
{ } else if (i2p::context.SupportsV6()) found = true;
if (i2p::context.SupportsV4 ()) found = true; }
} }
else if (ep.address ().is_v6 ()) if (found) break;
{ it++;
if (i2p::util::net::IsYggdrasilAddress (ep.address ())) }
{ if (!found) {
if (i2p::context.SupportsMesh ()) found = true; LogPrint(eLogError, "Timestamp: can't find compatible address for ", address);
} return;
else if (i2p::context.SupportsV6 ()) found = true; }
}
}
if (found) break;
it++;
}
if (!found)
{
LogPrint (eLogError, "Timestamp: can't find compatible address for ", address);
return;
}
boost::asio::ip::udp::socket socket (service); boost::asio::ip::udp::socket socket(service);
socket.open (ep.protocol (), ec); socket.open(ep.protocol(), ec);
if (!ec) if (!ec) {
{ uint8_t buf[48];// 48 bytes NTP request/response
uint8_t buf[48];// 48 bytes NTP request/response memset(buf, 0, 48);
memset (buf, 0, 48); htobe32buf(buf, (3 << 27) | (3 << 24)); // RFC 4330
htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330 size_t len = 0;
size_t len = 0; try {
try socket.send_to(boost::asio::buffer(buf, 48), ep);
{ int i = 0;
socket.send_to (boost::asio::buffer (buf, 48), ep); while (!socket.available() && i < 10) // 10 seconds max
int i = 0; {
while (!socket.available() && i < 10) // 10 seconds max std::this_thread::sleep_for(std::chrono::seconds(1));
{ i++;
std::this_thread::sleep_for (std::chrono::seconds(1)); }
i++; if (socket.available())
} len = socket.receive_from(boost::asio::buffer(buf, 48), ep);
if (socket.available ()) }
len = socket.receive_from (boost::asio::buffer (buf, 48), ep); catch (std::exception &e) {
} LogPrint(eLogError, "Timestamp: NTP error: ", e.what());
catch (std::exception& e) }
{ if (len >= 8) {
LogPrint (eLogError, "Timestamp: NTP error: ", e.what ()); auto ourTs = GetLocalSecondsSinceEpoch();
} uint32_t ts = bufbe32toh(buf + 32);
if (len >= 8) if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
{ g_TimeOffset = ts - ourTs;
auto ourTs = GetLocalSecondsSinceEpoch (); LogPrint(eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset,
uint32_t ts = bufbe32toh (buf + 32); " seconds");
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900 }
g_TimeOffset = ts - ourTs; } else
LogPrint (eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset, " seconds"); LogPrint(eLogError, "Timestamp: Couldn't open UDP socket");
} } else
} LogPrint(eLogError, "Timestamp: Couldn't resolve address ", address);
else }
LogPrint (eLogError, "Timestamp: Couldn't open UDP socket");
}
else
LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address);
}
NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service) NTPTimeSync::NTPTimeSync() : m_IsRunning(false), m_Timer(m_Service) {
{ i2p::config::GetOption("nettime.ntpsyncinterval", m_SyncInterval);
i2p::config::GetOption("nettime.ntpsyncinterval", m_SyncInterval); std::string ntpservers;
std::string ntpservers; i2p::config::GetOption("nettime.ntpservers", ntpservers); i2p::config::GetOption("nettime.ntpservers", ntpservers);
boost::split (m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on); boost::split(m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on);
} }
NTPTimeSync::~NTPTimeSync () NTPTimeSync::~NTPTimeSync() {
{ Stop();
Stop (); }
}
void NTPTimeSync::Start() void NTPTimeSync::Start() {
{ if (m_NTPServersList.size() > 0) {
if (m_NTPServersList.size () > 0) m_IsRunning = true;
{ LogPrint(eLogInfo, "Timestamp: NTP time sync starting");
m_IsRunning = true; m_Service.post(std::bind(&NTPTimeSync::Sync, this));
LogPrint(eLogInfo, "Timestamp: NTP time sync starting"); m_Thread.reset(new std::thread(std::bind(&NTPTimeSync::Run, this)));
m_Service.post (std::bind (&NTPTimeSync::Sync, this)); } else
m_Thread.reset (new std::thread (std::bind (&NTPTimeSync::Run, this))); LogPrint(eLogWarning, "Timestamp: No NTP server found");
} }
else
LogPrint (eLogWarning, "Timestamp: No NTP server found");
}
void NTPTimeSync::Stop () void NTPTimeSync::Stop() {
{ if (m_IsRunning) {
if (m_IsRunning) LogPrint(eLogInfo, "Timestamp: NTP time sync stopping");
{ m_IsRunning = false;
LogPrint(eLogInfo, "Timestamp: NTP time sync stopping"); m_Timer.cancel();
m_IsRunning = false; m_Service.stop();
m_Timer.cancel (); if (m_Thread) {
m_Service.stop (); m_Thread->join();
if (m_Thread) m_Thread.reset(nullptr);
{ }
m_Thread->join (); }
m_Thread.reset (nullptr); }
}
}
}
void NTPTimeSync::Run () void NTPTimeSync::Run() {
{ i2p::util::SetThreadName("Timesync");
i2p::util::SetThreadName("Timesync");
while (m_IsRunning) while (m_IsRunning) {
{ try {
try m_Service.run();
{ }
m_Service.run (); catch (std::exception &ex) {
} LogPrint(eLogError, "Timestamp: NTP time sync exception: ", ex.what());
catch (std::exception& ex) }
{ }
LogPrint (eLogError, "Timestamp: NTP time sync exception: ", ex.what ()); }
}
}
}
void NTPTimeSync::Sync () void NTPTimeSync::Sync() {
{ if (m_NTPServersList.size() > 0)
if (m_NTPServersList.size () > 0) SyncTimeWithNTP(m_NTPServersList[rand() % m_NTPServersList.size()]);
SyncTimeWithNTP (m_NTPServersList[rand () % m_NTPServersList.size ()]); else
else m_IsRunning = false;
m_IsRunning = false;
if (m_IsRunning) if (m_IsRunning) {
{ m_Timer.expires_from_now(boost::posix_time::hours(m_SyncInterval));
m_Timer.expires_from_now (boost::posix_time::hours (m_SyncInterval)); m_Timer.async_wait([this](const boost::system::error_code &ecode) {
m_Timer.async_wait ([this](const boost::system::error_code& ecode) if (ecode != boost::asio::error::operation_aborted)
{ Sync();
if (ecode != boost::asio::error::operation_aborted) });
Sync (); }
}); }
}
}
uint64_t GetMillisecondsSinceEpoch () uint64_t GetMillisecondsSinceEpoch() {
{ return GetLocalMillisecondsSinceEpoch() + g_TimeOffset * 1000;
return GetLocalMillisecondsSinceEpoch () + g_TimeOffset*1000; }
}
uint64_t GetSecondsSinceEpoch () uint64_t GetSecondsSinceEpoch() {
{ return GetLocalSecondsSinceEpoch() + g_TimeOffset;
return GetLocalSecondsSinceEpoch () + g_TimeOffset; }
}
uint32_t GetMinutesSinceEpoch () uint32_t GetMinutesSinceEpoch() {
{ return GetLocalMinutesSinceEpoch() + g_TimeOffset / 60;
return GetLocalMinutesSinceEpoch () + g_TimeOffset/60; }
}
uint32_t GetHoursSinceEpoch () uint32_t GetHoursSinceEpoch() {
{ return GetLocalHoursSinceEpoch() + g_TimeOffset / 3600;
return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; }
}
void GetCurrentDate (char * date) void GetCurrentDate(char *date) {
{ GetDateString(GetSecondsSinceEpoch(), date);
GetDateString (GetSecondsSinceEpoch (), date); }
}
void GetDateString (uint64_t timestamp, char * date) void GetDateString(uint64_t timestamp, char *date) {
{ using clock = std::chrono::system_clock;
using clock = std::chrono::system_clock; auto t = clock::to_time_t(clock::time_point(std::chrono::seconds(timestamp)));
auto t = clock::to_time_t (clock::time_point (std::chrono::seconds(timestamp))); struct tm tm;
struct tm tm;
#ifdef _WIN32 #ifdef _WIN32
gmtime_s(&tm, &t); gmtime_s(&tm, &t);
sprintf_s(date, 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); sprintf_s(date, 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#else #else
gmtime_r(&t, &tm); gmtime_r(&t, &tm);
sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif #endif
} }
void AdjustTimeOffset (int64_t offset) void AdjustTimeOffset(int64_t offset) {
{ g_TimeOffset += offset;
g_TimeOffset += offset; }
} }
}
} }

View file

@ -15,44 +15,48 @@
#include <string> #include <string>
#include <boost/asio.hpp> #include <boost/asio.hpp>
namespace i2p namespace i2p {
{ namespace util {
namespace util uint64_t GetMillisecondsSinceEpoch();
{
uint64_t GetMillisecondsSinceEpoch ();
uint64_t GetSecondsSinceEpoch ();
uint32_t GetMinutesSinceEpoch ();
uint32_t GetHoursSinceEpoch ();
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes uint64_t GetSecondsSinceEpoch();
void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
void AdjustTimeOffset (int64_t offset); // in seconds from current
class NTPTimeSync uint32_t GetMinutesSinceEpoch();
{
public:
NTPTimeSync (); uint32_t GetHoursSinceEpoch();
~NTPTimeSync ();
void Start (); void GetCurrentDate(char *date); // returns date as YYYYMMDD string, 9 bytes
void Stop (); void GetDateString(uint64_t timestamp,
char *date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
void AdjustTimeOffset(int64_t offset); // in seconds from current
private: class NTPTimeSync {
public:
void Run (); NTPTimeSync();
void Sync ();
private: ~NTPTimeSync();
bool m_IsRunning; void Start();
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service; void Stop();
boost::asio::deadline_timer m_Timer;
int m_SyncInterval; private:
std::vector<std::string> m_NTPServersList;
}; void Run();
}
void Sync();
private:
bool m_IsRunning;
std::unique_ptr <std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
int m_SyncInterval;
std::vector <std::string> m_NTPServersList;
};
}
} }
#endif #endif

View file

@ -15,104 +15,88 @@
#include "Transports.h" #include "Transports.h"
#include "TransitTunnel.h" #include "TransitTunnel.h"
namespace i2p namespace i2p {
{ namespace tunnel {
namespace tunnel TransitTunnel::TransitTunnel(uint32_t receiveTunnelID,
{ const uint8_t *nextIdent, uint32_t nextTunnelID,
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, const uint8_t *layerKey, const uint8_t *ivKey) :
const uint8_t * nextIdent, uint32_t nextTunnelID, TunnelBase(receiveTunnelID, nextTunnelID, nextIdent) {
const uint8_t * layerKey,const uint8_t * ivKey): m_Encryption.SetKeys(layerKey, ivKey);
TunnelBase (receiveTunnelID, nextTunnelID, nextIdent) }
{
m_Encryption.SetKeys (layerKey, ivKey);
}
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) void TransitTunnel::EncryptTunnelMsg(std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) {
{ m_Encryption.Encrypt(in->GetPayload() + 4, out->GetPayload() + 4);
m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); i2p::transport::transports.UpdateTotalTransitTransmittedBytes(TUNNEL_DATA_MSG_SIZE);
i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE); }
}
TransitTunnelParticipant::~TransitTunnelParticipant () TransitTunnelParticipant::~TransitTunnelParticipant() {
{ }
}
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) void TransitTunnelParticipant::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
{ EncryptTunnelMsg(tunnelMsg, tunnelMsg);
EncryptTunnelMsg (tunnelMsg, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength (); m_NumTransmittedBytes += tunnelMsg->GetLength();
htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ()); htobe32buf(tunnelMsg->GetPayload(), GetNextTunnelID());
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); tunnelMsg->FillI2NPMessageHeader(eI2NPTunnelData);
m_TunnelDataMsgs.push_back (tunnelMsg); m_TunnelDataMsgs.push_back(tunnelMsg);
} }
void TransitTunnelParticipant::FlushTunnelDataMsgs () void TransitTunnelParticipant::FlushTunnelDataMsgs() {
{ if (!m_TunnelDataMsgs.empty()) {
if (!m_TunnelDataMsgs.empty ()) auto num = m_TunnelDataMsgs.size();
{ if (num > 1)
auto num = m_TunnelDataMsgs.size (); LogPrint(eLogDebug, "TransitTunnel: ", GetTunnelID(), "->", GetNextTunnelID(), " ", num);
if (num > 1) i2p::transport::transports.SendMessages(GetNextIdentHash(), m_TunnelDataMsgs);
LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num); m_TunnelDataMsgs.clear();
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); }
m_TunnelDataMsgs.clear (); }
}
}
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) void TransitTunnel::SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg) {
{ LogPrint(eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID());
LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); }
}
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) void TransitTunnel::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
{ LogPrint(eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID());
LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); }
}
void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) void TransitTunnelGateway::SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg) {
{ TunnelMessageBlock block;
TunnelMessageBlock block; block.deliveryType = eDeliveryTypeLocal;
block.deliveryType = eDeliveryTypeLocal; block.data = msg;
block.data = msg; std::unique_lock<std::mutex> l(m_SendMutex);
std::unique_lock<std::mutex> l(m_SendMutex); m_Gateway.PutTunnelDataMsg(block);
m_Gateway.PutTunnelDataMsg (block); }
}
void TransitTunnelGateway::FlushTunnelDataMsgs () void TransitTunnelGateway::FlushTunnelDataMsgs() {
{ std::unique_lock<std::mutex> l(m_SendMutex);
std::unique_lock<std::mutex> l(m_SendMutex); m_Gateway.SendBuffer();
m_Gateway.SendBuffer (); }
}
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) void TransitTunnelEndpoint::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
{ auto newMsg = CreateEmptyTunnelDataMsg(true);
auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg(tunnelMsg, newMsg);
EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); LogPrint(eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID());
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); m_Endpoint.HandleDecryptedTunnelDataMsg(newMsg);
} }
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, std::shared_ptr<TransitTunnel> CreateTransitTunnel(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, const uint8_t *layerKey, const uint8_t *ivKey,
bool isGateway, bool isEndpoint) bool isGateway, bool isEndpoint) {
{ if (isEndpoint) {
if (isEndpoint) LogPrint(eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
{ return std::make_shared<TransitTunnelEndpoint>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); ivKey);
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); } else if (isGateway) {
} LogPrint(eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created");
else if (isGateway) return std::make_shared<TransitTunnelGateway>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
{ ivKey);
LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created"); } else {
return std::make_shared<TransitTunnelGateway> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); LogPrint(eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
} return std::make_shared<TransitTunnelParticipant>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
else ivKey);
{ }
LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); }
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); }
}
}
}
} }

View file

@ -19,95 +19,98 @@
#include "TunnelGateway.h" #include "TunnelGateway.h"
#include "TunnelBase.h" #include "TunnelBase.h"
namespace i2p namespace i2p {
{ namespace tunnel {
namespace tunnel class TransitTunnel : public TunnelBase {
{ public:
class TransitTunnel: public TunnelBase
{
public:
TransitTunnel (uint32_t receiveTunnelID, 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); const uint8_t *layerKey, const uint8_t *ivKey);
virtual size_t GetNumTransmittedBytes () const { return 0; }; virtual size_t GetNumTransmittedBytes() const { return 0; };
// implements TunnelBase // implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); void SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg);
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
private:
i2p::crypto::TunnelEncryption m_Encryption; void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
};
class TransitTunnelParticipant: public TransitTunnel void EncryptTunnelMsg(std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
{
public:
TransitTunnelParticipant (uint32_t receiveTunnelID, private:
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {};
~TransitTunnelParticipant ();
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; i2p::crypto::TunnelEncryption m_Encryption;
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg); };
void FlushTunnelDataMsgs ();
private: class TransitTunnelParticipant : public TransitTunnel {
public:
size_t m_NumTransmittedBytes; TransitTunnelParticipant(uint32_t receiveTunnelID,
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs; const uint8_t *nextIdent, uint32_t nextTunnelID,
}; const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes(0) {};
class TransitTunnelGateway: public TransitTunnel ~TransitTunnelParticipant();
{
public:
TransitTunnelGateway (uint32_t receiveTunnelID, size_t GetNumTransmittedBytes() const { return m_NumTransmittedBytes; };
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
void FlushTunnelDataMsgs ();
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
private: void FlushTunnelDataMsgs();
std::mutex m_SendMutex; private:
TunnelGateway m_Gateway;
};
class TransitTunnelEndpoint: public TransitTunnel size_t m_NumTransmittedBytes;
{ std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
public: };
TransitTunnelEndpoint (uint32_t receiveTunnelID, class TransitTunnelGateway : public TransitTunnel {
const uint8_t * nextIdent, uint32_t nextTunnelID, public:
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
void Cleanup () { m_Endpoint.Cleanup (); } TransitTunnelGateway(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {};
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg); void SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
private: void FlushTunnelDataMsgs();
TunnelEndpoint m_Endpoint; size_t GetNumTransmittedBytes() const { return m_Gateway.GetNumSentBytes(); };
};
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, private:
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey, std::mutex m_SendMutex;
bool isGateway, bool isEndpoint); TunnelGateway m_Gateway;
} };
class TransitTunnelEndpoint : public TransitTunnel {
public:
TransitTunnelEndpoint(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint(false) {}; // transit endpoint is always outbound
void Cleanup() { m_Endpoint.Cleanup(); }
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
size_t GetNumTransmittedBytes() const { return m_Endpoint.GetNumReceivedBytes(); }
private:
TunnelEndpoint m_Endpoint;
};
std::shared_ptr<TransitTunnel> CreateTransitTunnel(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey,
bool isGateway, bool isEndpoint);
}
} }
#endif #endif

View file

@ -20,111 +20,112 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Timestamp.h" #include "Timestamp.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport const size_t IPV4_HEADER_SIZE = 20;
{ const size_t IPV6_HEADER_SIZE = 40;
const size_t IPV4_HEADER_SIZE = 20; const size_t UDP_HEADER_SIZE = 8;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
class SignedData
{
public:
SignedData () {} class SignedData {
SignedData (const SignedData& other) public:
{
m_Stream << other.m_Stream.rdbuf ();
}
void Reset () SignedData() {}
{
m_Stream.str("");
}
void Insert (const uint8_t * buf, size_t len)
{
m_Stream.write ((char *)buf, len);
}
template<typename T> SignedData(const SignedData &other) {
void Insert (T t) m_Stream << other.m_Stream.rdbuf();
{ }
m_Stream.write ((char *)&t, sizeof (T));
}
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const void Reset() {
{ m_Stream.str("");
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); }
}
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const void Insert(const uint8_t *buf, size_t len) {
{ m_Stream.write((char *) buf, len);
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); }
}
private: template<typename T>
void Insert(T t) {
m_Stream.write((char *) &t, sizeof(T));
}
std::stringstream m_Stream; bool Verify(std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t *signature) const {
}; return ident->Verify((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature);
}
class TransportSession
{
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout): void Sign(const i2p::data::PrivateKeys &keys, uint8_t *signature) const {
m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), keys.Sign((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature);
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()) }
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
m_CreationTime = m_LastActivityTimestamp;
}
virtual ~TransportSession () {}; private:
virtual void Done () = 0;
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; } std::stringstream m_Stream;
};
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () class TransportSession {
{ public:
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
return m_RemoteIdentity;
}
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident)
{
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
m_RemoteIdentity = ident;
}
size_t GetNumSentBytes () const { return m_NumSentBytes; }; TransportSession(std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout) :
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; m_NumSentBytes(0), m_NumReceivedBytes(0), m_IsOutgoing(router),
bool IsOutgoing () const { return m_IsOutgoing; }; m_TerminationTimeout(terminationTimeout),
m_LastActivityTimestamp(i2p::util::GetSecondsSinceEpoch()) {
if (router)
m_RemoteIdentity = router->GetRouterIdentity();
m_CreationTime = m_LastActivityTimestamp;
}
int GetTerminationTimeout () const { return m_TerminationTimeout; }; virtual ~TransportSession() {};
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
uint32_t GetCreationTime () const { return m_CreationTime; }; virtual void Done() = 0;
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag () const { return 0; };
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
protected: std::string GetIdentHashBase64() const {
return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : "";
}
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity() {
mutable std::mutex m_RemoteIdentityMutex; std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
size_t m_NumSentBytes, m_NumReceivedBytes; return m_RemoteIdentity;
bool m_IsOutgoing; }
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp; void SetRemoteIdentity(std::shared_ptr<const i2p::data::IdentityEx> ident) {
uint32_t m_CreationTime; // seconds since epoch std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
}; m_RemoteIdentity = ident;
} }
size_t GetNumSentBytes() const { return m_NumSentBytes; };
size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
bool IsOutgoing() const { return m_IsOutgoing; };
int GetTerminationTimeout() const { return m_TerminationTimeout; };
void SetTerminationTimeout(int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
bool IsTerminationTimeoutExpired(uint64_t ts) const {
return ts >= m_LastActivityTimestamp + GetTerminationTimeout();
};
uint32_t GetCreationTime() const { return m_CreationTime; };
void SetCreationTime(uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag() const { return 0; };
virtual void SendLocalRouterInfo(bool update = false) { SendI2NPMessages({CreateDatabaseStoreMsg()}); };
virtual void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs) = 0;
protected:
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
mutable std::mutex m_RemoteIdentityMutex;
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch
};
}
} }
#endif #endif

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more