diff --git a/i18n/Afrikaans.cpp b/i18n/Afrikaans.cpp index 5860facf..87a5324a 100644 --- a/i18n/Afrikaans.cpp +++ b/i18n/Afrikaans.cpp @@ -14,68 +14,66 @@ // Afrikaans localization file -namespace i2p -{ -namespace i18n -{ -namespace afrikaans // language namespace -{ - // language name in lowercase - static std::string language = "afrikaans"; +namespace i2p { + namespace i18n { + namespace afrikaans // language namespace + { + // language name in lowercase + static std::string language = "afrikaans"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n != 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n != 1 ? 1 : 0; + } - static std::map strings - { - {"failed", "Het misluk"}, - {"unknown", "onbekend"}, - {"Tunnels", "Tonnels"}, - {"I2P tunnels", "I2P tonnels"}, - {"SAM sessions", "SAM sessies"}, - {"OK", "LEKKER"}, - {"Testing", "Besig om te toets"}, - {"Firewalled", "Vuurmuur'd"}, - {"Unknown", "Onbekend"}, - {"Error", "Fout"}, - {"Offline", "Aflyn"}, - {"Uptime", "Optyd"}, - {"Network status", "Netwerk status"}, - {"Network status v6", "Netwerk status v6"}, - {"Family", "Familie"}, - {"Received", "Ontvang"}, - {"Sent", "Gestuur"}, - {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."}, - {"Router Ident", "Router Ident"}, - {"Router Family", "Router Familie"}, - {"Enabled", "Geaktiveer"}, - {"Disabled", "Gedeaktiveer"}, - {"Change", "Verander"}, - {"Change language", "Verander taal"}, - {"Description", "Beskrywing"}, - {"Submit", "Stuur"}, - {"Proxy error", "Proxy-fout"}, - {"Host", "Gasheer"}, - {"", ""}, - }; + static std::map strings + { + {"failed", "Het misluk"}, + {"unknown", "onbekend"}, + {"Tunnels", "Tonnels"}, + {"I2P tunnels", "I2P tonnels"}, + {"SAM sessions", "SAM sessies"}, + {"OK", "LEKKER"}, + {"Testing", "Besig om te toets"}, + {"Firewalled", "Vuurmuur'd"}, + {"Unknown", "Onbekend"}, + {"Error", "Fout"}, + {"Offline", "Aflyn"}, + {"Uptime", "Optyd"}, + {"Network status", "Netwerk status"}, + {"Network status v6", "Netwerk status v6"}, + {"Family", "Familie"}, + {"Received", "Ontvang"}, + {"Sent", "Gestuur"}, + {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."}, + {"Router Ident", "Router Ident"}, + {"Router Family", "Router Familie"}, + {"Enabled", "Geaktiveer"}, + {"Disabled", "Gedeaktiveer"}, + {"Change", "Verander"}, + {"Change language", "Verander taal"}, + {"Description", "Beskrywing"}, + {"Submit", "Stuur"}, + {"Proxy error", "Proxy-fout"}, + {"Host", "Gasheer"}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"dag", "dae"}}, - {"hours", {"uur", "ure"}}, - {"minutes", {"minuut", "minute"}}, - {"seconds", {"seconde", "sekondes"}}, - {"", {"", ""}}, - }; + static std::map > plurals + { + {"days", {"dag", "dae"}}, + {"hours", {"uur", "ure"}}, + {"minutes", {"minuut", "minute"}}, + {"seconds", {"seconde", "sekondes"}}, + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/Armenian.cpp b/i18n/Armenian.cpp index 586e7579..dc727ffd 100644 --- a/i18n/Armenian.cpp +++ b/i18n/Armenian.cpp @@ -14,202 +14,200 @@ // Armenian localization file -namespace i2p -{ -namespace i18n -{ -namespace armenian // language namespace -{ - // language name in lowercase - static std::string language = "armenian"; +namespace i2p { + namespace i18n { + namespace armenian // language namespace + { + // language name in lowercase + static std::string language = "armenian"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n != 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n != 1 ? 1 : 0; + } - static std::map strings - { - {"KiB", "ԿիԲ"}, - {"MiB", "ՄիԲ"}, - {"GiB", "ԳիԲ"}, - {"building", "կառուցվում է"}, - {"failed", "Անհաջող"}, - {"expiring", "Լրանում է"}, - {"established", "կարգավոյված է"}, - {"unknown", "անհայտ"}, - {"exploratory", "հետազոտոկան"}, - {"i2pd webconsole", "Վեբ-կոնսոլ i2pd"}, - {"Main page", "Գլխավոր էջ"}, - {"Router commands", "Երթուղիչի հրահանգներ"}, - {"Local Destinations", "Տեղական վերջնակետերը"}, - {"LeaseSets", "ԼիզՍեթեր"}, - {"Tunnels", "Թունելներ"}, - {"Transit Tunnels", "Տարանցիկ թունելներ"}, - {"Transports", "Տրանսպորտ"}, - {"I2P tunnels", "I2P թունելներ"}, - {"SAM sessions", "SAM նստաշրջաններ"}, - {"ERROR", "ՍԽԱԼ"}, - {"OK", "ԼԱՎ"}, - {"Testing", "Փորձարկում"}, - {"Firewalled", "Արգելափակված է դրսից"}, - {"Unknown", "Անհայտ"}, - {"Proxy", "Պրոկսի"}, - {"Mesh", "MESH-ցանց"}, - {"Error", "Սխալ"}, - {"Clock skew", "Ոչ ճշգրիտ ժամանակ"}, - {"Offline", "Օֆլայն"}, - {"Symmetric NAT", "Սիմետրիկ NAT"}, - {"Uptime", "Առկայություն"}, - {"Network status", "Ցանցի կարգավիճակ"}, - {"Network status v6", "Ցանցի կարգավիճակ v6"}, - {"Stopping in", "Դադարում"}, - {"Family", "Խմբատեսակ"}, - {"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"}, - {"Received", "Ստացվել է"}, - {"KiB/s", "ԿիԲ/վ"}, - {"Sent", "Ուղարկվել է"}, - {"Transit", "Տարանցում"}, - {"Data path", "Տվյալների ուղին"}, - {"Hidden content. Press on text to see.", "Թաքցված բովանդակություն: Տեսնելու համար սեղմեկ տեքստին:"}, - {"Router Ident", "Երթուղիչի նույնականացուցիչ"}, - {"Router Family", "Երթուղիչի խումբը"}, - {"Router Caps", "Երթուղիչի հատկություններ"}, - {"Version", "Տարբերակ"}, - {"Our external address", "Մեր արտաքին հասցեն"}, - {"supported", "համատեղելի է"}, - {"Routers", "Երթուղիչներ"}, - {"Floodfills", "Floodfills-ներ"}, - {"Client Tunnels", "Oգտատիրական թունելներ"}, - {"Services", "Ծառայություններ"}, - {"Enabled", "Միացված է"}, - {"Disabled", "Անջատված է"}, - {"Encrypted B33 address", "Գաղտնագրված B33 հասցեներ"}, - {"Address registration line", "Հասցեի գրանցման տող"}, - {"Domain", "Տիրույթ"}, - {"Generate", "Գեներացնել"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", " Նշում. արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (example.i2p): Ենթատիրույթներ գրանցելու համար խնդրում ենք օգտագործել i2pd-tools գործիքակազմը"}, - {"Address", "Հասցե"}, - {"Type", "Տեսակը"}, - {"EncType", "Գաղտնագրի տեսակը"}, - {"Inbound tunnels", "Մուտքային թունելներ"}, - {"ms", "մլվ"}, - {"Outbound tunnels", "Ելքային թունելներ"}, - {"Tags", "Թեգեր"}, - {"Incoming", "Մուտքային"}, - {"Outgoing", "ելքային"}, - {"Destination", "Նշանակման վայր"}, - {"Amount", "Քանակ"}, - {"Incoming Tags", "Մուտքային պիտակներ"}, - {"Tags sessions", "Նստաշրջանի պիտակներ"}, - {"Status", "Կարգավիճակ"}, - {"Local Destination", "Տեղական նշանակման կետ"}, - {"Streams", "Հոսքեր"}, - {"Close stream", "Փակել հոսքը"}, - {"I2CP session not found", "I2CP նստաշրջանը գոյություն չունի"}, - {"I2CP is not enabled", "I2CP միացված է"}, - {"Invalid", "Անվավեր"}, - {"Store type", "Պահեստավորման տեսակը"}, - {"Expires", "Սպառվում է"}, - {"Non Expired Leases", "Չսպառված Lease-եր"}, - {"Gateway", "Դարպաս"}, - {"TunnelID", "Թունելի ID"}, - {"EndDate", "Ավարտ"}, - {"not floodfill", "ոչ floodfill-ներ"}, - {"Queue size", "Հերթի չափսը"}, - {"Run peer test", "Գործարկել փորձարկումը"}, - {"Decline transit tunnels", "Մերժել տարանցիկ թունելներ"}, - {"Accept transit tunnels", "Ընդունել տարանցիկ թունելներ"}, - {"Cancel graceful shutdown", "Չեղարկել սահուն անջատումը"}, - {"Start graceful shutdown", "Սկսել սահուն անջատումը"}, - {"Force shutdown", "Հարկադիր անջատում"}, - {"Reload external CSS styles", "Վերաբեռնեք CSS ոճաթերթը"}, - {"Note: any action done here are not persistent and not changes your config files.", " Նշում․ այստեղ կատարված ցանկացած գործողություն մշտական ​​չէ և չի փոխում ձեր կազմաձևման ֆայլերը։"}, - {"Logging level", "Գրառման աստիճանը"}, - {"Transit tunnels limit", "Տարանցիկ թունելների սահմանափակում"}, - {"Change", "Փոփոխել"}, - {"Change language", "Փոփոխել լեզուն"}, - {"no transit tunnels currently built", "ընթացիկ կառուցված տարանցիկ թունելներ գոյություն չունեն"}, - {"SAM disabled", "SAM-ն անջատված է"}, - {"no sessions currently running", "ներկայումս գործող նստաշրջաններ գոյություն չունեն"}, - {"SAM session not found", "SAM նստաշրջան գոյություն չունի"}, - {"SAM Session", "SAM նստաշրջան"}, - {"Server Tunnels", "Սերվերային թունելներ"}, - {"Client Forwards", "Օգտատիրական փոխանցումներ"}, - {"Server Forwards", "Սերվերային փոխանցումներ"}, - {"Unknown page", "Անհայտ էջ"}, - {"Invalid token", "Սխալ տոկեն"}, - {"SUCCESS", "ՀԱՋՈՂՎԱԾ"}, - {"Stream closed", "Հոսքն անջատված է"}, - {"Stream not found or already was closed", "Հոսքը գոյություն չունի կամ արդեն ավարտված է"}, - {"Destination not found", "Հասցեի վայրը չի գտնվել"}, - {"StreamID can't be null", "StreamID-ն չի կարող լինել դատարկ"}, - {"Return to destination page", "Վերադառնալ նախորդ էջի հասցե"}, - {"You will be redirected in 5 seconds", "Դուք կտեղափոխվեք 5 վայրկյանից"}, - {"Transit tunnels count must not exceed 65535", "Տարանցիկ թունելների քանակը չպետք է գերազանցի 65535-ը"}, - {"Back to commands list", "Վերադառնալ հրահանգների ցուցակ"}, - {"Register at reg.i2p", "Գրանցել reg.i2p-ում"}, - {"Description", "Նկարագրություն"}, - {"A bit information about service on domain", "Մի փոքր տեղեկատվություն տիրոիյթում գտնվող ծառայության մասին"}, - {"Submit", "Ուղարկվել"}, - {"Domain can't end with .b32.i2p", "Տիրույթը չպետք է վերջանա .b32.i2p-ով"}, - {"Domain must end with .i2p", "Տիրույթը պետք է վերջանա .i2p-ով"}, - {"Such destination is not found", "Այդիպսի հասցե գոյություն չունի"}, - {"Unknown command", "Անհայտ հրահանգ"}, - {"Command accepted", "Հրարահանգն ընդունված է"}, - {"Proxy error", "Պրոկսի սխալ"}, - {"Proxy info", "Պրոկսի տեղեկություն"}, - {"Proxy error: Host not found", "Պրոկսի սխալ՝ նման հոսթ գոյություն չունի"}, - {"Remote host not found in router's addressbook", "Դեպի հոսթ կատարված հարցումը գոյություն չունի երթուղիչի հասցեագրքում"}, - {"You may try to find this host on jump services below", "Ստորև Դուք կարող եք գտնել այս հոսթը jump ծառայությունների միջոցով"}, - {"Invalid request", "Սխալ հարցում"}, - {"Proxy unable to parse your request", "Պրոկսին չի կարող հասկանալ Ձեր հարցումը"}, - {"addresshelper is not supported", "addresshelper-ը համատեղելի չէ"}, - {"Host", "Հոսթ"}, - {"added to router's addressbook from helper", "Ավելացված է երթուղիչի հասցեագրքում helper-ի միջոցով"}, - {"Click here to proceed:", "Շարունակելու համար սեղմեք այստեղ"}, - {"Continue", "Շարունակել"}, - {"Addresshelper found", "addresshelper-ը գնտված է"}, - {"already in router's addressbook", "արդեն առկա է երթուղիչի հասցեագրքում"}, - {"Click here to update record:", "Սեղմեկ այստեղ որպեսզի թարվացնեք գրառումը"}, - {"invalid request uri", "Սխալ ձևավորված URI հարցում"}, - {"Can't detect destination host from request", "Չհաջողվեց հայնտաբերեկ վայրի հասցեն նշված հարցմամբ"}, - {"Outproxy failure", "Սխալ արտաքին պրոքսի"}, - {"bad outproxy settings", "Սխալ արտաքին պրոկսի կարգավորումներ"}, - {"not inside I2P network, but outproxy is not enabled", "Հարցումը I2P ցանցից դուրս է, բայց արտաքին պրոքսին միացված չէ"}, - {"unknown outproxy url", "արտաքին պրոքսիի անհայտ URL"}, - {"cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"}, - {"hostname too long", "Հոսթի անունը չափազանց երկար է"}, - {"cannot connect to upstream socks proxy", "չհաջողվեց միանալ վերադաս socks պրոկսիին"}, - {"Cannot negotiate with socks proxy", "Չհաջողվեց պայմանավորվել վերադաս socks պրոկսիի հետ"}, - {"CONNECT error", "Սխալ CONNECT հարցում"}, - {"Failed to Connect", "Միանալ չhաջողվեց"}, - {"socks proxy error", "Սխալ SOCKS պրոկսի"}, - {"failed to send request to upstream", "Չհաջողվեց հարցումն ուղարկել վերադաս պրոկսիին"}, - {"No Reply From socks proxy", "Բացակայում է պատասխանը SOCKS պրոկսի սերվերի կողմից"}, - {"cannot connect", "Հնարավոր չե միանալ"}, - {"http out proxy not implemented", "Արտաքին http պրոկսին դեռ իրականացված չէ"}, - {"cannot connect to upstream http proxy", "Չհաջողվեց միանալ վերադաս http պրոկսի սերվերին"}, - {"Host is down", "Հոսթն անհասանելի է"}, - {"Can't create connection to requested host, it may be down. Please try again later.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ"}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "ԿիԲ"}, + {"MiB", "ՄիԲ"}, + {"GiB", "ԳիԲ"}, + {"building", "կառուցվում է"}, + {"failed", "Անհաջող"}, + {"expiring", "Լրանում է"}, + {"established", "կարգավոյված է"}, + {"unknown", "անհայտ"}, + {"exploratory", "հետազոտոկան"}, + {"i2pd webconsole", "Վեբ-կոնսոլ i2pd"}, + {"Main page", "Գլխավոր էջ"}, + {"Router commands", "Երթուղիչի հրահանգներ"}, + {"Local Destinations", "Տեղական վերջնակետերը"}, + {"LeaseSets", "ԼիզՍեթեր"}, + {"Tunnels", "Թունելներ"}, + {"Transit Tunnels", "Տարանցիկ թունելներ"}, + {"Transports", "Տրանսպորտ"}, + {"I2P tunnels", "I2P թունելներ"}, + {"SAM sessions", "SAM նստաշրջաններ"}, + {"ERROR", "ՍԽԱԼ"}, + {"OK", "ԼԱՎ"}, + {"Testing", "Փորձարկում"}, + {"Firewalled", "Արգելափակված է դրսից"}, + {"Unknown", "Անհայտ"}, + {"Proxy", "Պրոկսի"}, + {"Mesh", "MESH-ցանց"}, + {"Error", "Սխալ"}, + {"Clock skew", "Ոչ ճշգրիտ ժամանակ"}, + {"Offline", "Օֆլայն"}, + {"Symmetric NAT", "Սիմետրիկ NAT"}, + {"Uptime", "Առկայություն"}, + {"Network status", "Ցանցի կարգավիճակ"}, + {"Network status v6", "Ցանցի կարգավիճակ v6"}, + {"Stopping in", "Դադարում"}, + {"Family", "Խմբատեսակ"}, + {"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"}, + {"Received", "Ստացվել է"}, + {"KiB/s", "ԿիԲ/վ"}, + {"Sent", "Ուղարկվել է"}, + {"Transit", "Տարանցում"}, + {"Data path", "Տվյալների ուղին"}, + {"Hidden content. Press on text to see.", "Թաքցված բովանդակություն: Տեսնելու համար սեղմեկ տեքստին:"}, + {"Router Ident", "Երթուղիչի նույնականացուցիչ"}, + {"Router Family", "Երթուղիչի խումբը"}, + {"Router Caps", "Երթուղիչի հատկություններ"}, + {"Version", "Տարբերակ"}, + {"Our external address", "Մեր արտաքին հասցեն"}, + {"supported", "համատեղելի է"}, + {"Routers", "Երթուղիչներ"}, + {"Floodfills", "Floodfills-ներ"}, + {"Client Tunnels", "Oգտատիրական թունելներ"}, + {"Services", "Ծառայություններ"}, + {"Enabled", "Միացված է"}, + {"Disabled", "Անջատված է"}, + {"Encrypted B33 address", "Գաղտնագրված B33 հասցեներ"}, + {"Address registration line", "Հասցեի գրանցման տող"}, + {"Domain", "Տիրույթ"}, + {"Generate", "Գեներացնել"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", " Նշում. արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (example.i2p): Ենթատիրույթներ գրանցելու համար խնդրում ենք օգտագործել i2pd-tools գործիքակազմը"}, + {"Address", "Հասցե"}, + {"Type", "Տեսակը"}, + {"EncType", "Գաղտնագրի տեսակը"}, + {"Inbound tunnels", "Մուտքային թունելներ"}, + {"ms", "մլվ"}, + {"Outbound tunnels", "Ելքային թունելներ"}, + {"Tags", "Թեգեր"}, + {"Incoming", "Մուտքային"}, + {"Outgoing", "ելքային"}, + {"Destination", "Նշանակման վայր"}, + {"Amount", "Քանակ"}, + {"Incoming Tags", "Մուտքային պիտակներ"}, + {"Tags sessions", "Նստաշրջանի պիտակներ"}, + {"Status", "Կարգավիճակ"}, + {"Local Destination", "Տեղական նշանակման կետ"}, + {"Streams", "Հոսքեր"}, + {"Close stream", "Փակել հոսքը"}, + {"I2CP session not found", "I2CP նստաշրջանը գոյություն չունի"}, + {"I2CP is not enabled", "I2CP միացված է"}, + {"Invalid", "Անվավեր"}, + {"Store type", "Պահեստավորման տեսակը"}, + {"Expires", "Սպառվում է"}, + {"Non Expired Leases", "Չսպառված Lease-եր"}, + {"Gateway", "Դարպաս"}, + {"TunnelID", "Թունելի ID"}, + {"EndDate", "Ավարտ"}, + {"not floodfill", "ոչ floodfill-ներ"}, + {"Queue size", "Հերթի չափսը"}, + {"Run peer test", "Գործարկել փորձարկումը"}, + {"Decline transit tunnels", "Մերժել տարանցիկ թունելներ"}, + {"Accept transit tunnels", "Ընդունել տարանցիկ թունելներ"}, + {"Cancel graceful shutdown", "Չեղարկել սահուն անջատումը"}, + {"Start graceful shutdown", "Սկսել սահուն անջատումը"}, + {"Force shutdown", "Հարկադիր անջատում"}, + {"Reload external CSS styles", "Վերաբեռնեք CSS ոճաթերթը"}, + {"Note: any action done here are not persistent and not changes your config files.", " Նշում․ այստեղ կատարված ցանկացած գործողություն մշտական ​​չէ և չի փոխում ձեր կազմաձևման ֆայլերը։"}, + {"Logging level", "Գրառման աստիճանը"}, + {"Transit tunnels limit", "Տարանցիկ թունելների սահմանափակում"}, + {"Change", "Փոփոխել"}, + {"Change language", "Փոփոխել լեզուն"}, + {"no transit tunnels currently built", "ընթացիկ կառուցված տարանցիկ թունելներ գոյություն չունեն"}, + {"SAM disabled", "SAM-ն անջատված է"}, + {"no sessions currently running", "ներկայումս գործող նստաշրջաններ գոյություն չունեն"}, + {"SAM session not found", "SAM նստաշրջան գոյություն չունի"}, + {"SAM Session", "SAM նստաշրջան"}, + {"Server Tunnels", "Սերվերային թունելներ"}, + {"Client Forwards", "Օգտատիրական փոխանցումներ"}, + {"Server Forwards", "Սերվերային փոխանցումներ"}, + {"Unknown page", "Անհայտ էջ"}, + {"Invalid token", "Սխալ տոկեն"}, + {"SUCCESS", "ՀԱՋՈՂՎԱԾ"}, + {"Stream closed", "Հոսքն անջատված է"}, + {"Stream not found or already was closed", "Հոսքը գոյություն չունի կամ արդեն ավարտված է"}, + {"Destination not found", "Հասցեի վայրը չի գտնվել"}, + {"StreamID can't be null", "StreamID-ն չի կարող լինել դատարկ"}, + {"Return to destination page", "Վերադառնալ նախորդ էջի հասցե"}, + {"You will be redirected in 5 seconds", "Դուք կտեղափոխվեք 5 վայրկյանից"}, + {"Transit tunnels count must not exceed 65535", "Տարանցիկ թունելների քանակը չպետք է գերազանցի 65535-ը"}, + {"Back to commands list", "Վերադառնալ հրահանգների ցուցակ"}, + {"Register at reg.i2p", "Գրանցել reg.i2p-ում"}, + {"Description", "Նկարագրություն"}, + {"A bit information about service on domain", "Մի փոքր տեղեկատվություն տիրոիյթում գտնվող ծառայության մասին"}, + {"Submit", "Ուղարկվել"}, + {"Domain can't end with .b32.i2p", "Տիրույթը չպետք է վերջանա .b32.i2p-ով"}, + {"Domain must end with .i2p", "Տիրույթը պետք է վերջանա .i2p-ով"}, + {"Such destination is not found", "Այդիպսի հասցե գոյություն չունի"}, + {"Unknown command", "Անհայտ հրահանգ"}, + {"Command accepted", "Հրարահանգն ընդունված է"}, + {"Proxy error", "Պրոկսի սխալ"}, + {"Proxy info", "Պրոկսի տեղեկություն"}, + {"Proxy error: Host not found", "Պրոկսի սխալ՝ նման հոսթ գոյություն չունի"}, + {"Remote host not found in router's addressbook", "Դեպի հոսթ կատարված հարցումը գոյություն չունի երթուղիչի հասցեագրքում"}, + {"You may try to find this host on jump services below", "Ստորև Դուք կարող եք գտնել այս հոսթը jump ծառայությունների միջոցով"}, + {"Invalid request", "Սխալ հարցում"}, + {"Proxy unable to parse your request", "Պրոկսին չի կարող հասկանալ Ձեր հարցումը"}, + {"addresshelper is not supported", "addresshelper-ը համատեղելի չէ"}, + {"Host", "Հոսթ"}, + {"added to router's addressbook from helper", "Ավելացված է երթուղիչի հասցեագրքում helper-ի միջոցով"}, + {"Click here to proceed:", "Շարունակելու համար սեղմեք այստեղ"}, + {"Continue", "Շարունակել"}, + {"Addresshelper found", "addresshelper-ը գնտված է"}, + {"already in router's addressbook", "արդեն առկա է երթուղիչի հասցեագրքում"}, + {"Click here to update record:", "Սեղմեկ այստեղ որպեսզի թարվացնեք գրառումը"}, + {"invalid request uri", "Սխալ ձևավորված URI հարցում"}, + {"Can't detect destination host from request", "Չհաջողվեց հայնտաբերեկ վայրի հասցեն նշված հարցմամբ"}, + {"Outproxy failure", "Սխալ արտաքին պրոքսի"}, + {"bad outproxy settings", "Սխալ արտաքին պրոկսի կարգավորումներ"}, + {"not inside I2P network, but outproxy is not enabled", "Հարցումը I2P ցանցից դուրս է, բայց արտաքին պրոքսին միացված չէ"}, + {"unknown outproxy url", "արտաքին պրոքսիի անհայտ URL"}, + {"cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"}, + {"hostname too long", "Հոսթի անունը չափազանց երկար է"}, + {"cannot connect to upstream socks proxy", "չհաջողվեց միանալ վերադաս socks պրոկսիին"}, + {"Cannot negotiate with socks proxy", "Չհաջողվեց պայմանավորվել վերադաս socks պրոկսիի հետ"}, + {"CONNECT error", "Սխալ CONNECT հարցում"}, + {"Failed to Connect", "Միանալ չhաջողվեց"}, + {"socks proxy error", "Սխալ SOCKS պրոկսի"}, + {"failed to send request to upstream", "Չհաջողվեց հարցումն ուղարկել վերադաս պրոկսիին"}, + {"No Reply From socks proxy", "Բացակայում է պատասխանը SOCKS պրոկսի սերվերի կողմից"}, + {"cannot connect", "Հնարավոր չե միանալ"}, + {"http out proxy not implemented", "Արտաքին http պրոկսին դեռ իրականացված չէ"}, + {"cannot connect to upstream http proxy", "Չհաջողվեց միանալ վերադաս http պրոկսի սերվերին"}, + {"Host is down", "Հոսթն անհասանելի է"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ"}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"օր", "օր"}}, - {"hours", {"ժամ", "ժամ"}}, - {"minutes", {"րոպե", "րոպե"}}, - {"seconds", {"վարկյան", "վարկյան"}}, - {"", {"", ""}}, - }; + static std::map > plurals + { + {"days", {"օր", "օր"}}, + {"hours", {"ժամ", "ժամ"}}, + {"minutes", {"րոպե", "րոպե"}}, + {"seconds", {"վարկյան", "վարկյան"}}, + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/Chinese.cpp b/i18n/Chinese.cpp index 8a55ab6a..4bc2cdcc 100644 --- a/i18n/Chinese.cpp +++ b/i18n/Chinese.cpp @@ -15,203 +15,201 @@ // Simplified Chinese localization file // This is an example translation file without strings in it. -namespace i2p -{ -namespace i18n -{ -namespace chinese // language namespace -{ - // language name in lowercase - static std::string language = "chinese"; +namespace i2p { + namespace i18n { + namespace chinese // language namespace + { + // language name in lowercase + static std::string language = "chinese"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return 0; + } - static std::map strings - { - {"KiB", "KiB"}, - {"MiB", "MiB"}, - {"GiB", "GiB"}, - {"building", "正在构建"}, - {"failed", "连接失败"}, - {"expiring", "即将过期"}, - {"established", "连接已建立"}, - {"unknown", "未知"}, - {"exploratory", "探测"}, - {"Purple I2P Webconsole", "Purple I2P 网页控制台"}, - {"i2pd webconsole", "i2pd 网页控制台"}, - {"Main page", "主页"}, - {"Router commands", "路由命令"}, - {"Local Destinations", "本地目标"}, - {"LeaseSets", "租契集"}, - {"Tunnels", "隧道"}, - {"Transit Tunnels", "中转隧道"}, - {"Transports", "传输"}, - {"I2P tunnels", "I2P 隧道"}, - {"SAM sessions", "SAM 会话"}, - {"ERROR", "错误"}, - {"OK", "良好"}, - {"Testing", "测试中"}, - {"Firewalled", "受到防火墙限制"}, - {"Unknown", "未知"}, - {"Proxy", "代理"}, - {"Mesh", "Mesh组网"}, - {"Error", "错误"}, - {"Clock skew", "时钟偏移"}, - {"Offline", "离线"}, - {"Symmetric NAT", "对称 NAT"}, - {"Uptime", "运行时间"}, - {"Network status", "IPv4 网络状态"}, - {"Network status v6", "IPv6 网络状态"}, - {"Stopping in", "距停止还有:"}, - {"Family", "家族"}, - {"Tunnel creation success rate", "隧道创建成功率"}, - {"Received", "已接收"}, - {"KiB/s", "KiB/s"}, - {"Sent", "已发送"}, - {"Transit", "中转"}, - {"Data path", "数据文件路径"}, - {"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"}, - {"Router Ident", "路由身份"}, - {"Router Family", "路由器家族"}, - {"Router Caps", "路由器类型"}, - {"Version", "版本"}, - {"Our external address", "外部地址"}, - {"supported", "支持"}, - {"Routers", "路由节点"}, - {"Floodfills", "洪泛节点"}, - {"Client Tunnels", "客户端隧道"}, - {"Services", "服务"}, - {"Enabled", "启用"}, - {"Disabled", "禁用"}, - {"Encrypted B33 address", "加密的 B33 地址"}, - {"Address registration line", "地址域名注册"}, - {"Domain", "域名"}, - {"Generate", "生成"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "注意: 结果字符串只能用于注册次级域名(例如:example.i2p)。若需注册子域名,请使用 i2pd-tools。"}, - {"Address", "地址"}, - {"Type", "类型"}, - {"EncType", "加密类型"}, - {"Inbound tunnels", "入站隧道"}, - {"ms", "毫秒"}, - {"Outbound tunnels", "出站隧道"}, - {"Tags", "标签"}, - {"Incoming", "传入"}, - {"Outgoing", "传出"}, - {"Destination", "目标"}, - {"Amount", "数量"}, - {"Incoming Tags", "传入标签"}, - {"Tags sessions", "标签会话"}, - {"Status", "状态"}, - {"Local Destination", "本地目标"}, - {"Streams", "流"}, - {"Close stream", "断开流"}, - {"I2CP session not found", "未找到 I2CP 会话"}, - {"I2CP is not enabled", "I2CP 未启用"}, - {"Invalid", "无效"}, - {"Store type", "存储类型"}, - {"Expires", "过期时间"}, - {"Non Expired Leases", "未到期的租约"}, - {"Gateway", "网关"}, - {"TunnelID", "隧道 ID"}, - {"EndDate", "结束日期"}, - {"not floodfill", "非洪泛"}, - {"Queue size", "队列大小"}, - {"Run peer test", "运行节点测试"}, - {"Decline transit tunnels", "拒绝中转隧道"}, - {"Accept transit tunnels", "允许中转隧道"}, - {"Cancel graceful shutdown", "取消优雅地离线"}, - {"Start graceful shutdown", "优雅地离线"}, - {"Force shutdown", "强制停止"}, - {"Reload external CSS styles", "重载外部 CSS 样式"}, - {"Note: any action done here are not persistent and not changes your config files.", "注意: 此处完成的任何操作都不是永久的,不会更改您的配置文件。"}, - {"Logging level", "日志记录级别"}, - {"Transit tunnels limit", "中转隧道限制"}, - {"Change", "更改"}, - {"Change language", "更改语言"}, - {"no transit tunnels currently built", "目前未构建中转隧道"}, - {"SAM disabled", "SAM 已禁用"}, - {"no sessions currently running", "没有正在运行的会话"}, - {"SAM session not found", "未找到 SAM 会话"}, - {"SAM Session", "SAM 会话"}, - {"Server Tunnels", "服务器隧道"}, - {"Client Forwards", "客户端转发"}, - {"Server Forwards", "服务器转发"}, - {"Unknown page", "未知页面"}, - {"Invalid token", "无效令牌"}, - {"SUCCESS", "成功"}, - {"Stream closed", "流已关闭"}, - {"Stream not found or already was closed", "流未找到或已关闭"}, - {"Destination not found", "找不到目标"}, - {"StreamID can't be null", "StreamID 不能为空"}, - {"Return to destination page", "返回目标页面"}, - {"You will be redirected in 5 seconds", "您将在5秒内被重定向"}, - {"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"}, - {"Back to commands list", "返回命令列表"}, - {"Register at reg.i2p", "在 reg.i2p 注册域名"}, - {"Description", "描述"}, - {"A bit information about service on domain", "在此域名上运行的服务的一些信息"}, - {"Submit", "提交"}, - {"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"}, - {"Domain must end with .i2p", "域名必须以 .i2p 结尾"}, - {"Such destination is not found", "找不到此目标"}, - {"Unknown command", "未知指令"}, - {"Command accepted", "已接受指令"}, - {"Proxy error", "代理错误"}, - {"Proxy info", "代理信息"}, - {"Proxy error: Host not found", "代理错误:未找到主机"}, - {"Remote host not found in router's addressbook", "在路由地址簿中未找到远程主机"}, - {"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务中找到该主机"}, - {"Invalid request", "无效请求"}, - {"Proxy unable to parse your request", "代理无法解析您的请求"}, - {"addresshelper is not supported", "不支持地址助手"}, - {"Host", "主机"}, - {"added to router's addressbook from helper", "将此地址从地址助手添加到路由地址簿"}, - {"Click here to proceed:", "点击此处继续:"}, - {"Continue", "继续"}, - {"Addresshelper found", "已找到地址助手"}, - {"already in router's addressbook", "已在路由地址簿中"}, - {"Click here to update record:", "点击此处更新地址簿记录"}, - {"invalid request uri", "无效的 URL 请求"}, - {"Can't detect destination host from request", "无法从请求中检测到目标主机"}, - {"Outproxy failure", "出口代理故障"}, - {"bad outproxy settings", "错误的出口代理设置"}, - {"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"}, - {"unknown outproxy url", "未知的出口代理地址"}, - {"cannot resolve upstream proxy", "无法解析上游代理"}, - {"hostname too long", "主机名过长"}, - {"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"}, - {"Cannot negotiate with socks proxy", "无法与 socks 代理协商"}, - {"CONNECT error", "连接错误"}, - {"Failed to Connect", "连接失败"}, - {"socks proxy error", "socks 代理错误"}, - {"failed to send request to upstream", "向上游发送请求失败"}, - {"No Reply From socks proxy", "没有来自 socks 代理的回复"}, - {"cannot connect", "无法连接"}, - {"http out proxy not implemented", "http 出口代理未实现"}, - {"cannot connect to upstream http proxy", "无法连接到上游 http 代理"}, - {"Host is down", "主机已关闭"}, - {"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "正在构建"}, + {"failed", "连接失败"}, + {"expiring", "即将过期"}, + {"established", "连接已建立"}, + {"unknown", "未知"}, + {"exploratory", "探测"}, + {"Purple I2P Webconsole", "Purple I2P 网页控制台"}, + {"i2pd webconsole", "i2pd 网页控制台"}, + {"Main page", "主页"}, + {"Router commands", "路由命令"}, + {"Local Destinations", "本地目标"}, + {"LeaseSets", "租契集"}, + {"Tunnels", "隧道"}, + {"Transit Tunnels", "中转隧道"}, + {"Transports", "传输"}, + {"I2P tunnels", "I2P 隧道"}, + {"SAM sessions", "SAM 会话"}, + {"ERROR", "错误"}, + {"OK", "良好"}, + {"Testing", "测试中"}, + {"Firewalled", "受到防火墙限制"}, + {"Unknown", "未知"}, + {"Proxy", "代理"}, + {"Mesh", "Mesh组网"}, + {"Error", "错误"}, + {"Clock skew", "时钟偏移"}, + {"Offline", "离线"}, + {"Symmetric NAT", "对称 NAT"}, + {"Uptime", "运行时间"}, + {"Network status", "IPv4 网络状态"}, + {"Network status v6", "IPv6 网络状态"}, + {"Stopping in", "距停止还有:"}, + {"Family", "家族"}, + {"Tunnel creation success rate", "隧道创建成功率"}, + {"Received", "已接收"}, + {"KiB/s", "KiB/s"}, + {"Sent", "已发送"}, + {"Transit", "中转"}, + {"Data path", "数据文件路径"}, + {"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"}, + {"Router Ident", "路由身份"}, + {"Router Family", "路由器家族"}, + {"Router Caps", "路由器类型"}, + {"Version", "版本"}, + {"Our external address", "外部地址"}, + {"supported", "支持"}, + {"Routers", "路由节点"}, + {"Floodfills", "洪泛节点"}, + {"Client Tunnels", "客户端隧道"}, + {"Services", "服务"}, + {"Enabled", "启用"}, + {"Disabled", "禁用"}, + {"Encrypted B33 address", "加密的 B33 地址"}, + {"Address registration line", "地址域名注册"}, + {"Domain", "域名"}, + {"Generate", "生成"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "注意: 结果字符串只能用于注册次级域名(例如:example.i2p)。若需注册子域名,请使用 i2pd-tools。"}, + {"Address", "地址"}, + {"Type", "类型"}, + {"EncType", "加密类型"}, + {"Inbound tunnels", "入站隧道"}, + {"ms", "毫秒"}, + {"Outbound tunnels", "出站隧道"}, + {"Tags", "标签"}, + {"Incoming", "传入"}, + {"Outgoing", "传出"}, + {"Destination", "目标"}, + {"Amount", "数量"}, + {"Incoming Tags", "传入标签"}, + {"Tags sessions", "标签会话"}, + {"Status", "状态"}, + {"Local Destination", "本地目标"}, + {"Streams", "流"}, + {"Close stream", "断开流"}, + {"I2CP session not found", "未找到 I2CP 会话"}, + {"I2CP is not enabled", "I2CP 未启用"}, + {"Invalid", "无效"}, + {"Store type", "存储类型"}, + {"Expires", "过期时间"}, + {"Non Expired Leases", "未到期的租约"}, + {"Gateway", "网关"}, + {"TunnelID", "隧道 ID"}, + {"EndDate", "结束日期"}, + {"not floodfill", "非洪泛"}, + {"Queue size", "队列大小"}, + {"Run peer test", "运行节点测试"}, + {"Decline transit tunnels", "拒绝中转隧道"}, + {"Accept transit tunnels", "允许中转隧道"}, + {"Cancel graceful shutdown", "取消优雅地离线"}, + {"Start graceful shutdown", "优雅地离线"}, + {"Force shutdown", "强制停止"}, + {"Reload external CSS styles", "重载外部 CSS 样式"}, + {"Note: any action done here are not persistent and not changes your config files.", "注意: 此处完成的任何操作都不是永久的,不会更改您的配置文件。"}, + {"Logging level", "日志记录级别"}, + {"Transit tunnels limit", "中转隧道限制"}, + {"Change", "更改"}, + {"Change language", "更改语言"}, + {"no transit tunnels currently built", "目前未构建中转隧道"}, + {"SAM disabled", "SAM 已禁用"}, + {"no sessions currently running", "没有正在运行的会话"}, + {"SAM session not found", "未找到 SAM 会话"}, + {"SAM Session", "SAM 会话"}, + {"Server Tunnels", "服务器隧道"}, + {"Client Forwards", "客户端转发"}, + {"Server Forwards", "服务器转发"}, + {"Unknown page", "未知页面"}, + {"Invalid token", "无效令牌"}, + {"SUCCESS", "成功"}, + {"Stream closed", "流已关闭"}, + {"Stream not found or already was closed", "流未找到或已关闭"}, + {"Destination not found", "找不到目标"}, + {"StreamID can't be null", "StreamID 不能为空"}, + {"Return to destination page", "返回目标页面"}, + {"You will be redirected in 5 seconds", "您将在5秒内被重定向"}, + {"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"}, + {"Back to commands list", "返回命令列表"}, + {"Register at reg.i2p", "在 reg.i2p 注册域名"}, + {"Description", "描述"}, + {"A bit information about service on domain", "在此域名上运行的服务的一些信息"}, + {"Submit", "提交"}, + {"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"}, + {"Domain must end with .i2p", "域名必须以 .i2p 结尾"}, + {"Such destination is not found", "找不到此目标"}, + {"Unknown command", "未知指令"}, + {"Command accepted", "已接受指令"}, + {"Proxy error", "代理错误"}, + {"Proxy info", "代理信息"}, + {"Proxy error: Host not found", "代理错误:未找到主机"}, + {"Remote host not found in router's addressbook", "在路由地址簿中未找到远程主机"}, + {"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务中找到该主机"}, + {"Invalid request", "无效请求"}, + {"Proxy unable to parse your request", "代理无法解析您的请求"}, + {"addresshelper is not supported", "不支持地址助手"}, + {"Host", "主机"}, + {"added to router's addressbook from helper", "将此地址从地址助手添加到路由地址簿"}, + {"Click here to proceed:", "点击此处继续:"}, + {"Continue", "继续"}, + {"Addresshelper found", "已找到地址助手"}, + {"already in router's addressbook", "已在路由地址簿中"}, + {"Click here to update record:", "点击此处更新地址簿记录"}, + {"invalid request uri", "无效的 URL 请求"}, + {"Can't detect destination host from request", "无法从请求中检测到目标主机"}, + {"Outproxy failure", "出口代理故障"}, + {"bad outproxy settings", "错误的出口代理设置"}, + {"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"}, + {"unknown outproxy url", "未知的出口代理地址"}, + {"cannot resolve upstream proxy", "无法解析上游代理"}, + {"hostname too long", "主机名过长"}, + {"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"}, + {"Cannot negotiate with socks proxy", "无法与 socks 代理协商"}, + {"CONNECT error", "连接错误"}, + {"Failed to Connect", "连接失败"}, + {"socks proxy error", "socks 代理错误"}, + {"failed to send request to upstream", "向上游发送请求失败"}, + {"No Reply From socks proxy", "没有来自 socks 代理的回复"}, + {"cannot connect", "无法连接"}, + {"http out proxy not implemented", "http 出口代理未实现"}, + {"cannot connect to upstream http proxy", "无法连接到上游 http 代理"}, + {"Host is down", "主机已关闭"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"天"}}, - {"hours", {"时"}}, - {"minutes", {"分"}}, - {"seconds", {"秒"}}, - {"", {""}}, - }; + static std::map > plurals + { + {"days", {"天"}}, + {"hours", {"时"}}, + {"minutes", {"分"}}, + {"seconds", {"秒"}}, + {"", {""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/English.cpp b/i18n/English.cpp index 2670e984..cf068dc7 100644 --- a/i18n/English.cpp +++ b/i18n/English.cpp @@ -15,36 +15,34 @@ // English localization file // This is an example translation file without strings in it. -namespace i2p -{ -namespace i18n -{ -namespace english // language namespace -{ - // language name in lowercase - static std::string language = "english"; +namespace i2p { + namespace i18n { + namespace english // language namespace + { + // language name in lowercase + static std::string language = "english"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n != 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n != 1 ? 1 : 0; + } - static std::map strings - { - {"", ""}, - }; + static std::map strings + { + {"", ""}, + }; - static std::map> plurals - { - {"", {"", ""}}, - }; + static std::map > plurals + { + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/French.cpp b/i18n/French.cpp index 1a49ddfc..f1d4a678 100644 --- a/i18n/French.cpp +++ b/i18n/French.cpp @@ -14,198 +14,196 @@ // French localization file -namespace i2p -{ -namespace i18n -{ -namespace french // language namespace -{ - // language name in lowercase - static std::string language = "french"; +namespace i2p { + namespace i18n { + namespace french // language namespace + { + // language name in lowercase + static std::string language = "french"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n != 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n != 1 ? 1 : 0; + } - static std::map strings - { - {"KiB", "Kio"}, - {"MiB", "Mio"}, - {"GiB", "Gio"}, - {"building", "En construction"}, - {"failed", "échoué"}, - {"expiring", "expiré"}, - {"established", "établi"}, - {"unknown", "inconnu"}, - {"exploratory", "exploratoire"}, - {"Purple I2P Webconsole", "Console web Purple I2P"}, - {"i2pd webconsole", "Console web i2pd"}, - {"Main page", "Page principale"}, - {"Router commands", "Commandes du routeur"}, - {"Local Destinations", "Destinations locales"}, - {"LeaseSets", "Jeu de baux"}, - {"Tunnels", "Tunnels"}, - {"Transit Tunnels", "Tunnels transitoires"}, - {"Transports", "Transports"}, - {"I2P tunnels", "Tunnels I2P"}, - {"SAM sessions", "Sessions SAM"}, - {"ERROR", "ERREUR"}, - {"OK", "OK"}, - {"Testing", "Test en cours"}, - {"Firewalled", "Derrière un pare-feu"}, - {"Unknown", "Inconnu"}, - {"Proxy", "Proxy"}, - {"Mesh", "Maillé"}, - {"Error", "Erreur"}, - {"Clock skew", "Horloge décalée"}, - {"Offline", "Hors ligne"}, - {"Symmetric NAT", "NAT symétrique"}, - {"Uptime", "Temps de fonctionnement"}, - {"Network status", "État du réseau"}, - {"Network status v6", "État du réseau v6"}, - {"Stopping in", "Arrêt dans"}, - {"Family", "Famille"}, - {"Tunnel creation success rate", "Taux de succès de création de tunnels"}, - {"Received", "Reçu"}, - {"KiB/s", "kio/s"}, - {"Sent", "Envoyé"}, - {"Transit", "Transité"}, - {"Data path", "Emplacement des données"}, - {"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."}, - {"Router Ident", "Identifiant du routeur"}, - {"Router Family", "Famille du routeur"}, - {"Router Caps", "Limiteurs du routeur"}, - {"Version", "Version"}, - {"Our external address", "Notre adresse externe"}, - {"supported", "supporté"}, - {"Routers", "Routeurs"}, - {"Client Tunnels", "Tunnels clients"}, - {"Services", "Services"}, - {"Enabled", "Activé"}, - {"Disabled", "Désactivé"}, - {"Encrypted B33 address", "Adresse B33 chiffrée"}, - {"Address registration line", "Ligne d'inscription de l'adresse"}, - {"Domain", "Domaine"}, - {"Generate", "Générer"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Note: 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"}, - {"Type", "Type"}, - {"Inbound tunnels", "Tunnels entrants"}, - {"ms", "ms"}, - {"Outbound tunnels", "Tunnels sortants"}, - {"Tags", "Balises"}, - {"Incoming", "Entrant"}, - {"Outgoing", "Sortant"}, - {"Destination", "Destination"}, - {"Amount", "Quantité"}, - {"Incoming Tags", "Balises entrantes"}, - {"Tags sessions", "Sessions des balises"}, - {"Status", "Statut"}, - {"Local Destination", "Destination locale"}, - {"Streams", "Flux"}, - {"Close stream", "Fermer le flux"}, - {"I2CP session not found", "Session I2CP introuvable"}, - {"I2CP is not enabled", "I2CP est désactivé"}, - {"Invalid", "Invalide"}, - {"Store type", "Type de stockage"}, - {"Expires", "Expire"}, - {"Non Expired Leases", "Baux non expirés"}, - {"Gateway", "Passerelle"}, - {"TunnelID", "ID du tunnel"}, - {"EndDate", "Date de fin"}, - {"Queue size", "Longueur de la file"}, - {"Run peer test", "Lancer test des pairs"}, - {"Decline transit tunnels", "Refuser les tunnels transitoires"}, - {"Accept transit tunnels", "Accepter les tunnels transitoires"}, - {"Cancel graceful shutdown", "Annuler l'arrêt gracieux"}, - {"Start graceful shutdown", "Démarrer l'arrêt gracieux"}, - {"Force shutdown", "Forcer l'arrêt"}, - {"Reload external CSS styles", "Rafraîchir les styles CSS externes"}, - {"Note: any action done here are not persistent and not changes your config files.", "Note: Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."}, - {"Logging level", "Niveau de journalisation"}, - {"Transit tunnels limit", "Limite sur les tunnels transitoires"}, - {"Change", "Changer"}, - {"Change language", "Changer la langue"}, - {"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"}, - {"SAM disabled", "SAM désactivé"}, - {"no sessions currently running", "aucune session présentement en cours"}, - {"SAM session not found", "session SAM introuvable"}, - {"SAM Session", "Session SAM"}, - {"Server Tunnels", "Tunnels serveurs"}, - {"Unknown page", "Page inconnue"}, - {"Invalid token", "Jeton invalide"}, - {"SUCCESS", "SUCCÈS"}, - {"Stream closed", "Flux fermé"}, - {"Stream not found or already was closed", "Flux introuvable ou déjà fermé"}, - {"Destination not found", "Destination introuvable"}, - {"StreamID can't be null", "StreamID ne peut pas être vide"}, - {"Return to destination page", "Retourner à la page de destination"}, - {"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"}, - {"Back to commands list", "Retour à la liste des commandes"}, - {"Register at reg.i2p", "Inscription à reg.i2p"}, - {"Description", "Description"}, - {"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"}, - {"Submit", "Soumettre"}, - {"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"}, - {"Such destination is not found", "Cette destination est introuvable"}, - {"Unknown command", "Commande inconnue"}, - {"Command accepted", "Commande acceptée"}, - {"Proxy error", "Erreur de proxy"}, - {"Proxy info", "Information sur le proxy"}, - {"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"}, - {"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"}, - {"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"}, - {"addresshelper is not supported", "Assistant d'adresse non supporté"}, - {"Host", "Hôte"}, - {"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:"}, - {"Continue", "Continuer"}, - {"Addresshelper found", "Assistant d'adresse trouvé"}, - {"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:"}, - {"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"}, - {"Outproxy failure", "Échec de 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é"}, - {"unknown outproxy url", "URL du proxy de sortie inconnu"}, - {"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"}, - {"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 negotiate with socks proxy", "Impossible de négocier avec le proxy socks"}, - {"CONNECT error", "Erreur de connexion"}, - {"Failed to Connect", "Échec de connexion"}, - {"socks proxy error", "Erreur de proxy socks"}, - {"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"}, - {"cannot connect", "impossible de connecter"}, - {"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"}, - {"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."}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "Kio"}, + {"MiB", "Mio"}, + {"GiB", "Gio"}, + {"building", "En construction"}, + {"failed", "échoué"}, + {"expiring", "expiré"}, + {"established", "établi"}, + {"unknown", "inconnu"}, + {"exploratory", "exploratoire"}, + {"Purple I2P Webconsole", "Console web Purple I2P"}, + {"i2pd webconsole", "Console web i2pd"}, + {"Main page", "Page principale"}, + {"Router commands", "Commandes du routeur"}, + {"Local Destinations", "Destinations locales"}, + {"LeaseSets", "Jeu de baux"}, + {"Tunnels", "Tunnels"}, + {"Transit Tunnels", "Tunnels transitoires"}, + {"Transports", "Transports"}, + {"I2P tunnels", "Tunnels I2P"}, + {"SAM sessions", "Sessions SAM"}, + {"ERROR", "ERREUR"}, + {"OK", "OK"}, + {"Testing", "Test en cours"}, + {"Firewalled", "Derrière un pare-feu"}, + {"Unknown", "Inconnu"}, + {"Proxy", "Proxy"}, + {"Mesh", "Maillé"}, + {"Error", "Erreur"}, + {"Clock skew", "Horloge décalée"}, + {"Offline", "Hors ligne"}, + {"Symmetric NAT", "NAT symétrique"}, + {"Uptime", "Temps de fonctionnement"}, + {"Network status", "État du réseau"}, + {"Network status v6", "État du réseau v6"}, + {"Stopping in", "Arrêt dans"}, + {"Family", "Famille"}, + {"Tunnel creation success rate", "Taux de succès de création de tunnels"}, + {"Received", "Reçu"}, + {"KiB/s", "kio/s"}, + {"Sent", "Envoyé"}, + {"Transit", "Transité"}, + {"Data path", "Emplacement des données"}, + {"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."}, + {"Router Ident", "Identifiant du routeur"}, + {"Router Family", "Famille du routeur"}, + {"Router Caps", "Limiteurs du routeur"}, + {"Version", "Version"}, + {"Our external address", "Notre adresse externe"}, + {"supported", "supporté"}, + {"Routers", "Routeurs"}, + {"Client Tunnels", "Tunnels clients"}, + {"Services", "Services"}, + {"Enabled", "Activé"}, + {"Disabled", "Désactivé"}, + {"Encrypted B33 address", "Adresse B33 chiffrée"}, + {"Address registration line", "Ligne d'inscription de l'adresse"}, + {"Domain", "Domaine"}, + {"Generate", "Générer"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Note: 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"}, + {"Type", "Type"}, + {"Inbound tunnels", "Tunnels entrants"}, + {"ms", "ms"}, + {"Outbound tunnels", "Tunnels sortants"}, + {"Tags", "Balises"}, + {"Incoming", "Entrant"}, + {"Outgoing", "Sortant"}, + {"Destination", "Destination"}, + {"Amount", "Quantité"}, + {"Incoming Tags", "Balises entrantes"}, + {"Tags sessions", "Sessions des balises"}, + {"Status", "Statut"}, + {"Local Destination", "Destination locale"}, + {"Streams", "Flux"}, + {"Close stream", "Fermer le flux"}, + {"I2CP session not found", "Session I2CP introuvable"}, + {"I2CP is not enabled", "I2CP est désactivé"}, + {"Invalid", "Invalide"}, + {"Store type", "Type de stockage"}, + {"Expires", "Expire"}, + {"Non Expired Leases", "Baux non expirés"}, + {"Gateway", "Passerelle"}, + {"TunnelID", "ID du tunnel"}, + {"EndDate", "Date de fin"}, + {"Queue size", "Longueur de la file"}, + {"Run peer test", "Lancer test des pairs"}, + {"Decline transit tunnels", "Refuser les tunnels transitoires"}, + {"Accept transit tunnels", "Accepter les tunnels transitoires"}, + {"Cancel graceful shutdown", "Annuler l'arrêt gracieux"}, + {"Start graceful shutdown", "Démarrer l'arrêt gracieux"}, + {"Force shutdown", "Forcer l'arrêt"}, + {"Reload external CSS styles", "Rafraîchir les styles CSS externes"}, + {"Note: any action done here are not persistent and not changes your config files.", "Note: Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."}, + {"Logging level", "Niveau de journalisation"}, + {"Transit tunnels limit", "Limite sur les tunnels transitoires"}, + {"Change", "Changer"}, + {"Change language", "Changer la langue"}, + {"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"}, + {"SAM disabled", "SAM désactivé"}, + {"no sessions currently running", "aucune session présentement en cours"}, + {"SAM session not found", "session SAM introuvable"}, + {"SAM Session", "Session SAM"}, + {"Server Tunnels", "Tunnels serveurs"}, + {"Unknown page", "Page inconnue"}, + {"Invalid token", "Jeton invalide"}, + {"SUCCESS", "SUCCÈS"}, + {"Stream closed", "Flux fermé"}, + {"Stream not found or already was closed", "Flux introuvable ou déjà fermé"}, + {"Destination not found", "Destination introuvable"}, + {"StreamID can't be null", "StreamID ne peut pas être vide"}, + {"Return to destination page", "Retourner à la page de destination"}, + {"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"}, + {"Back to commands list", "Retour à la liste des commandes"}, + {"Register at reg.i2p", "Inscription à reg.i2p"}, + {"Description", "Description"}, + {"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"}, + {"Submit", "Soumettre"}, + {"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"}, + {"Such destination is not found", "Cette destination est introuvable"}, + {"Unknown command", "Commande inconnue"}, + {"Command accepted", "Commande acceptée"}, + {"Proxy error", "Erreur de proxy"}, + {"Proxy info", "Information sur le proxy"}, + {"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"}, + {"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"}, + {"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"}, + {"addresshelper is not supported", "Assistant d'adresse non supporté"}, + {"Host", "Hôte"}, + {"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:"}, + {"Continue", "Continuer"}, + {"Addresshelper found", "Assistant d'adresse trouvé"}, + {"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:"}, + {"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"}, + {"Outproxy failure", "Échec de 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é"}, + {"unknown outproxy url", "URL du proxy de sortie inconnu"}, + {"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"}, + {"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 negotiate with socks proxy", "Impossible de négocier avec le proxy socks"}, + {"CONNECT error", "Erreur de connexion"}, + {"Failed to Connect", "Échec de connexion"}, + {"socks proxy error", "Erreur de proxy socks"}, + {"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"}, + {"cannot connect", "impossible de connecter"}, + {"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"}, + {"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."}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"jour", "jours"}}, - {"hours", {"heure", "heures"}}, - {"minutes", {"minute", "minutes"}}, - {"seconds", {"seconde", "secondes"}}, - {"", {"", ""}}, - }; + static std::map > plurals + { + {"days", {"jour", "jours"}}, + {"hours", {"heure", "heures"}}, + {"minutes", {"minute", "minutes"}}, + {"seconds", {"seconde", "secondes"}}, + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/German.cpp b/i18n/German.cpp index f114bf8e..75834768 100644 --- a/i18n/German.cpp +++ b/i18n/German.cpp @@ -14,203 +14,201 @@ // German localization file -namespace i2p -{ -namespace i18n -{ -namespace german // language namespace -{ - // language name in lowercase - static std::string language = "german"; +namespace i2p { + namespace i18n { + namespace german // language namespace + { + // language name in lowercase + static std::string language = "german"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n != 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n != 1 ? 1 : 0; + } - static std::map strings - { - {"Purple I2P Webconsole", "Purple-I2P-Webkonsole"}, - {"KiB", "KiB"}, - {"MiB", "MiB"}, - {"GiB", "GiB"}, - {"building", "In Bau"}, - {"failed", "fehlgeschlagen"}, - {"expiring", "läuft ab"}, - {"established", "hergestellt"}, - {"unknown", "Unbekannt"}, - {"exploratory", "erforschend"}, - {"i2pd webconsole", "i2pd-Webkonsole"}, - {"Main page", "Startseite"}, - {"Router commands", "Routerbefehle"}, - {"Local Destinations", "Lokale Ziele"}, - {"LeaseSets", "LeaseSets"}, - {"Tunnels", "Tunnel"}, - {"Transit Tunnels", "Transittunnel"}, - {"Transports", "Transporte"}, - {"I2P tunnels", "I2P-Tunnel"}, - {"SAM sessions", "SAM-Sitzungen"}, - {"ERROR", "FEHLER"}, - {"OK", "OK"}, - {"Testing", "Testen"}, - {"Firewalled", "Hinter einer Firewall"}, - {"Unknown", "Unbekannt"}, - {"Proxy", "Proxy"}, - {"Mesh", "Mesh"}, - {"Error", "Fehler"}, - {"Clock skew", "Zeitabweichung"}, - {"Offline", "Offline"}, - {"Symmetric NAT", "Symmetrisches NAT"}, - {"Uptime", "Laufzeit"}, - {"Network status", "Netzwerkstatus"}, - {"Network status v6", "Netzwerkstatus v6"}, - {"Stopping in", "Stoppt in"}, - {"Family", "Familie"}, - {"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"}, - {"Received", "Eingegangen"}, - {"KiB/s", "KiB/s"}, - {"Sent", "Gesendet"}, - {"Transit", "Transit"}, - {"Data path", "Datenpfad"}, - {"Hidden content. Press on text to see.", "Versteckter Inhalt. Klicke hier, um ihn zu sehen."}, - {"Router Ident", "Routeridentität"}, - {"Router Family", "Routerfamilie"}, - {"Router Caps", "Routerattribute"}, - {"Version", "Version"}, - {"Our external address", "Unsere externe Adresse"}, - {"supported", "unterstützt"}, - {"Routers", "Router"}, - {"Floodfills", "Floodfills"}, - {"Client Tunnels", "Clienttunnel"}, - {"Services", "Services"}, - {"Enabled", "Aktiviert"}, - {"Disabled", "Deaktiviert"}, - {"Encrypted B33 address", "Verschlüsselte B33-Adresse"}, - {"Address registration line", "Adressregistrierungszeile"}, - {"Domain", "Domain"}, - {"Generate", "Generieren"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Hinweis: 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"}, - {"Type", "Typ"}, - {"EncType", "Verschlüsselungstyp"}, - {"Inbound tunnels", "Eingehende Tunnel"}, - {"ms", "ms"}, - {"Outbound tunnels", "Ausgehende Tunnel"}, - {"Tags", "Tags"}, - {"Incoming", "Eingehend"}, - {"Outgoing", "Ausgehend"}, - {"Destination", "Ziel"}, - {"Amount", "Anzahl"}, - {"Incoming Tags", "Eingehende Tags"}, - {"Tags sessions", "Tags-Sitzungen"}, - {"Status", "Status"}, - {"Local Destination", "Lokales Ziel"}, - {"Streams", "Streams"}, - {"Close stream", "Stream schließen"}, - {"I2CP session not found", "I2CP-Sitzung nicht gefunden"}, - {"I2CP is not enabled", "I2CP ist nicht aktiviert"}, - {"Invalid", "Ungültig"}, - {"Store type", "Speichertyp"}, - {"Expires", "Ablaufdatum"}, - {"Non Expired Leases", "Nicht abgelaufene Leases"}, - {"Gateway", "Gateway"}, - {"TunnelID", "TunnelID"}, - {"EndDate", "Enddatum"}, - {"not floodfill", "kein Floodfill"}, - {"Queue size", "Größe der Warteschlange"}, - {"Run peer test", "Peer-Test durchführen"}, - {"Decline transit tunnels", "Transittunnel ablehnen"}, - {"Accept transit tunnels", "Transittunnel akzeptieren"}, - {"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"}, - {"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"}, - {"Force shutdown", "Herunterfahren erzwingen"}, - {"Reload external CSS styles", "Lade externe CSS-Stile neu"}, - {"Note: any action done here are not persistent and not changes your config files.", "Hinweis: Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."}, - {"Logging level", "Protokollierungslevel"}, - {"Transit tunnels limit", "Limit für Transittunnel"}, - {"Change", "Ändern"}, - {"Change language", "Sprache ändern"}, - {"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"}, - {"SAM disabled", "SAM deaktiviert"}, - {"no sessions currently running", "Derzeit keine laufenden Sitzungen"}, - {"SAM session not found", "SAM-Sitzung nicht gefunden"}, - {"SAM Session", "SAM-Sitzung"}, - {"Server Tunnels", "Servertunnel"}, - {"Client Forwards", "Client-Weiterleitungen"}, - {"Server Forwards", "Server-Weiterleitungen"}, - {"Unknown page", "Unbekannte Seite"}, - {"Invalid token", "Ungültiger Token"}, - {"SUCCESS", "ERFOLGREICH"}, - {"Stream closed", "Stream geschlossen"}, - {"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"}, - {"Destination not found", "Ziel nicht gefunden"}, - {"StreamID can't be null", "StreamID kann nicht null sein"}, - {"Return to destination page", "Zurück zur Ziel-Seite"}, - {"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"}, - {"Back to commands list", "Zurück zur Befehlsliste"}, - {"Register at reg.i2p", "Auf reg.i2p registrieren"}, - {"Description", "Beschreibung"}, - {"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"}, - {"Submit", "Absenden"}, - {"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"}, - {"Domain must end with .i2p", "Domain muss auf .i2p enden"}, - {"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"}, - {"Unknown command", "Unbekannter Befehl"}, - {"Command accepted", "Befehl akzeptiert"}, - {"Proxy error", "Proxy-Fehler"}, - {"Proxy info", "Proxy-Info"}, - {"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"}, - {"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"}, - {"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"}, - {"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"}, - {"Host", "Host"}, - {"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"}, - {"Click here to proceed:", "Klicke hier um fortzufahren:"}, - {"Continue", "Fortsetzen"}, - {"Addresshelper found", "Adresshelfer gefunden"}, - {"already in router's addressbook", "bereits im Adressbuch des Routers"}, - {"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"}, - {"invalid request uri", "ungültige Anfrage-URI"}, - {"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"}, - {"Outproxy failure", "Outproxy-Fehler"}, - {"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"}, - {"unknown outproxy url", "unbekannte Outproxy-URL"}, - {"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"}, - {"hostname too long", "Hostname zu lang"}, - {"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"}, - {"CONNECT error", "CONNECT-Fehler"}, - {"Failed to Connect", "Verbindung konnte nicht hergestellt werden"}, - {"socks proxy error", "Socks-Proxy-Fehler"}, - {"failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"}, - {"No Reply From socks proxy", "Keine Antwort vom Socks-Proxy"}, - {"cannot connect", "kann nicht verbinden"}, - {"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"}, - {"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"}, - {"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."}, - {"", ""}, - }; + static std::map strings + { + {"Purple I2P Webconsole", "Purple-I2P-Webkonsole"}, + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "In Bau"}, + {"failed", "fehlgeschlagen"}, + {"expiring", "läuft ab"}, + {"established", "hergestellt"}, + {"unknown", "Unbekannt"}, + {"exploratory", "erforschend"}, + {"i2pd webconsole", "i2pd-Webkonsole"}, + {"Main page", "Startseite"}, + {"Router commands", "Routerbefehle"}, + {"Local Destinations", "Lokale Ziele"}, + {"LeaseSets", "LeaseSets"}, + {"Tunnels", "Tunnel"}, + {"Transit Tunnels", "Transittunnel"}, + {"Transports", "Transporte"}, + {"I2P tunnels", "I2P-Tunnel"}, + {"SAM sessions", "SAM-Sitzungen"}, + {"ERROR", "FEHLER"}, + {"OK", "OK"}, + {"Testing", "Testen"}, + {"Firewalled", "Hinter einer Firewall"}, + {"Unknown", "Unbekannt"}, + {"Proxy", "Proxy"}, + {"Mesh", "Mesh"}, + {"Error", "Fehler"}, + {"Clock skew", "Zeitabweichung"}, + {"Offline", "Offline"}, + {"Symmetric NAT", "Symmetrisches NAT"}, + {"Uptime", "Laufzeit"}, + {"Network status", "Netzwerkstatus"}, + {"Network status v6", "Netzwerkstatus v6"}, + {"Stopping in", "Stoppt in"}, + {"Family", "Familie"}, + {"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"}, + {"Received", "Eingegangen"}, + {"KiB/s", "KiB/s"}, + {"Sent", "Gesendet"}, + {"Transit", "Transit"}, + {"Data path", "Datenpfad"}, + {"Hidden content. Press on text to see.", "Versteckter Inhalt. Klicke hier, um ihn zu sehen."}, + {"Router Ident", "Routeridentität"}, + {"Router Family", "Routerfamilie"}, + {"Router Caps", "Routerattribute"}, + {"Version", "Version"}, + {"Our external address", "Unsere externe Adresse"}, + {"supported", "unterstützt"}, + {"Routers", "Router"}, + {"Floodfills", "Floodfills"}, + {"Client Tunnels", "Clienttunnel"}, + {"Services", "Services"}, + {"Enabled", "Aktiviert"}, + {"Disabled", "Deaktiviert"}, + {"Encrypted B33 address", "Verschlüsselte B33-Adresse"}, + {"Address registration line", "Adressregistrierungszeile"}, + {"Domain", "Domain"}, + {"Generate", "Generieren"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Hinweis: 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"}, + {"Type", "Typ"}, + {"EncType", "Verschlüsselungstyp"}, + {"Inbound tunnels", "Eingehende Tunnel"}, + {"ms", "ms"}, + {"Outbound tunnels", "Ausgehende Tunnel"}, + {"Tags", "Tags"}, + {"Incoming", "Eingehend"}, + {"Outgoing", "Ausgehend"}, + {"Destination", "Ziel"}, + {"Amount", "Anzahl"}, + {"Incoming Tags", "Eingehende Tags"}, + {"Tags sessions", "Tags-Sitzungen"}, + {"Status", "Status"}, + {"Local Destination", "Lokales Ziel"}, + {"Streams", "Streams"}, + {"Close stream", "Stream schließen"}, + {"I2CP session not found", "I2CP-Sitzung nicht gefunden"}, + {"I2CP is not enabled", "I2CP ist nicht aktiviert"}, + {"Invalid", "Ungültig"}, + {"Store type", "Speichertyp"}, + {"Expires", "Ablaufdatum"}, + {"Non Expired Leases", "Nicht abgelaufene Leases"}, + {"Gateway", "Gateway"}, + {"TunnelID", "TunnelID"}, + {"EndDate", "Enddatum"}, + {"not floodfill", "kein Floodfill"}, + {"Queue size", "Größe der Warteschlange"}, + {"Run peer test", "Peer-Test durchführen"}, + {"Decline transit tunnels", "Transittunnel ablehnen"}, + {"Accept transit tunnels", "Transittunnel akzeptieren"}, + {"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"}, + {"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"}, + {"Force shutdown", "Herunterfahren erzwingen"}, + {"Reload external CSS styles", "Lade externe CSS-Stile neu"}, + {"Note: any action done here are not persistent and not changes your config files.", "Hinweis: Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."}, + {"Logging level", "Protokollierungslevel"}, + {"Transit tunnels limit", "Limit für Transittunnel"}, + {"Change", "Ändern"}, + {"Change language", "Sprache ändern"}, + {"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"}, + {"SAM disabled", "SAM deaktiviert"}, + {"no sessions currently running", "Derzeit keine laufenden Sitzungen"}, + {"SAM session not found", "SAM-Sitzung nicht gefunden"}, + {"SAM Session", "SAM-Sitzung"}, + {"Server Tunnels", "Servertunnel"}, + {"Client Forwards", "Client-Weiterleitungen"}, + {"Server Forwards", "Server-Weiterleitungen"}, + {"Unknown page", "Unbekannte Seite"}, + {"Invalid token", "Ungültiger Token"}, + {"SUCCESS", "ERFOLGREICH"}, + {"Stream closed", "Stream geschlossen"}, + {"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"}, + {"Destination not found", "Ziel nicht gefunden"}, + {"StreamID can't be null", "StreamID kann nicht null sein"}, + {"Return to destination page", "Zurück zur Ziel-Seite"}, + {"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"}, + {"Back to commands list", "Zurück zur Befehlsliste"}, + {"Register at reg.i2p", "Auf reg.i2p registrieren"}, + {"Description", "Beschreibung"}, + {"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"}, + {"Submit", "Absenden"}, + {"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"}, + {"Domain must end with .i2p", "Domain muss auf .i2p enden"}, + {"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"}, + {"Unknown command", "Unbekannter Befehl"}, + {"Command accepted", "Befehl akzeptiert"}, + {"Proxy error", "Proxy-Fehler"}, + {"Proxy info", "Proxy-Info"}, + {"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"}, + {"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"}, + {"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"}, + {"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"}, + {"Host", "Host"}, + {"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"}, + {"Click here to proceed:", "Klicke hier um fortzufahren:"}, + {"Continue", "Fortsetzen"}, + {"Addresshelper found", "Adresshelfer gefunden"}, + {"already in router's addressbook", "bereits im Adressbuch des Routers"}, + {"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"}, + {"invalid request uri", "ungültige Anfrage-URI"}, + {"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"}, + {"Outproxy failure", "Outproxy-Fehler"}, + {"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"}, + {"unknown outproxy url", "unbekannte Outproxy-URL"}, + {"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"}, + {"hostname too long", "Hostname zu lang"}, + {"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"}, + {"CONNECT error", "CONNECT-Fehler"}, + {"Failed to Connect", "Verbindung konnte nicht hergestellt werden"}, + {"socks proxy error", "Socks-Proxy-Fehler"}, + {"failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"}, + {"No Reply From socks proxy", "Keine Antwort vom Socks-Proxy"}, + {"cannot connect", "kann nicht verbinden"}, + {"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"}, + {"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"}, + {"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."}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"Tag", "Tage"}}, - {"hours", {"Stunde", "Stunden"}}, - {"minutes", {"Minute", "Minuten"}}, - {"seconds", {"Sekunde", "Sekunden"}}, - {"", {"", ""}}, - }; + static std::map > plurals + { + {"days", {"Tag", "Tage"}}, + {"hours", {"Stunde", "Stunden"}}, + {"minutes", {"Minute", "Minuten"}}, + {"seconds", {"Sekunde", "Sekunden"}}, + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/I18N.h b/i18n/I18N.h index dd804926..aa166636 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -11,35 +11,29 @@ #include "ClientContext.h" -namespace i2p -{ -namespace i18n -{ - inline void SetLanguage(const std::string &lang) - { - const auto it = i2p::i18n::languages.find(lang); - if (it == i2p::i18n::languages.end()) // fallback - i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); - else - i2p::client::context.SetLanguage (it->second.LocaleFunc()); - } +namespace i2p { + namespace i18n { + inline void SetLanguage(const std::string &lang) { + const auto it = i2p::i18n::languages.find(lang); + if (it == i2p::i18n::languages.end()) // fallback + i2p::client::context.SetLanguage(i2p::i18n::english::GetLocale()); + else + i2p::client::context.SetLanguage(it->second.LocaleFunc()); + } - inline std::string translate (const std::string& arg) - { - return i2p::client::context.GetLanguage ()->GetString (arg); - } + inline std::string translate(const std::string &arg) { + return i2p::client::context.GetLanguage()->GetString(arg); + } - inline std::string translate (const std::string& arg, const std::string& arg2, const int& n) - { - return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n); - } -} // i18n + inline std::string translate(const std::string &arg, const std::string &arg2, const int &n) { + return i2p::client::context.GetLanguage()->GetPlural(arg, arg2, n); + } + } // i18n } // i2p template -std::string tr (TArgs&&... args) -{ - return i2p::i18n::translate(std::forward(args)...); +std::string tr(TArgs &&... args) { + return i2p::i18n::translate(std::forward(args)...); } #endif // __I18N_H__ diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h index 87bc503e..77cd8af7 100644 --- a/i18n/I18N_langs.h +++ b/i18n/I18N_langs.h @@ -9,97 +9,85 @@ #ifndef __I18N_LANGS_H__ #define __I18N_LANGS_H__ -namespace i2p -{ -namespace i18n -{ - class Locale - { - public: - Locale ( - const std::string& language, - const std::map& strings, - const std::map>& plurals, - std::function formula - ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; +namespace i2p { + namespace i18n { + class Locale { + public: + Locale( + const std::string &language, + const std::map &strings, + const std::map > &plurals, + std::function formula + ) : m_Language(language), m_Strings(strings), m_Plurals(plurals), m_Formula(formula) {}; - // Get activated language name for webconsole - std::string GetLanguage() const - { - return m_Language; - } + // Get activated language name for webconsole + std::string GetLanguage() const { + return m_Language; + } - std::string GetString (const std::string& arg) const - { - const auto it = m_Strings.find(arg); - if (it == m_Strings.end()) - { - return arg; - } - else - { - return it->second; - } - } + std::string GetString(const std::string &arg) const { + const auto it = m_Strings.find(arg); + if (it == m_Strings.end()) { + return arg; + } else { + return it->second; + } + } - std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const - { - const auto it = m_Plurals.find(arg2); - if (it == m_Plurals.end()) // not found, fallback to english - { - return n == 1 ? arg : arg2; - } - else - { - int form = m_Formula(n); - return it->second[form]; - } - } + std::string GetPlural(const std::string &arg, const std::string &arg2, const int &n) const { + const auto it = m_Plurals.find(arg2); + if (it == m_Plurals.end()) // not found, fallback to english + { + return n == 1 ? arg : arg2; + } else { + int form = m_Formula(n); + return it->second[form]; + } + } - private: - const std::string m_Language; - const std::map m_Strings; - const std::map> m_Plurals; - std::function m_Formula; - }; + private: + const std::string m_Language; + const std::map m_Strings; + const std::map > m_Plurals; + std::function m_Formula; + }; - struct langData - { - std::string LocaleName; // localized name - std::string ShortCode; // short language code, like "en" - std::function (void)> LocaleFunc; - }; + struct langData { + std::string LocaleName; // localized name + std::string ShortCode; // short language code, like "en" + std::function(void)> LocaleFunc; + }; - // Add localization here with language name as namespace - namespace afrikaans { std::shared_ptr GetLocale (); } - namespace armenian { std::shared_ptr GetLocale (); } - namespace chinese { std::shared_ptr GetLocale (); } - namespace english { std::shared_ptr GetLocale (); } - namespace french { std::shared_ptr GetLocale (); } - namespace german { std::shared_ptr GetLocale (); } - namespace russian { std::shared_ptr GetLocale (); } - namespace turkmen { std::shared_ptr GetLocale (); } - namespace ukrainian { std::shared_ptr GetLocale (); } - namespace uzbek { std::shared_ptr GetLocale (); } + // Add localization here with language name as namespace + namespace afrikaans { std::shared_ptr GetLocale(); } + namespace armenian { std::shared_ptr GetLocale(); } + namespace chinese { std::shared_ptr GetLocale(); } + namespace english { std::shared_ptr GetLocale(); } + namespace french { std::shared_ptr GetLocale(); } + namespace german { std::shared_ptr GetLocale(); } + namespace russian { std::shared_ptr GetLocale(); } + namespace turkmen { std::shared_ptr GetLocale(); } + namespace ukrainian { std::shared_ptr GetLocale(); } + namespace uzbek { std::shared_ptr GetLocale(); } - /** - * That map contains international language name lower-case, name in it's language and it's code - */ - static std::map languages - { - { "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} }, - { "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} }, - { "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} }, - { "english", {"English", "en", i2p::i18n::english::GetLocale} }, - { "french", {"Français", "fr", i2p::i18n::french::GetLocale} }, - { "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} }, - { "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} }, - { "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, - { "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, - { "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} }, - }; + /** + * That map contains international language name lower-case, name in it's language and it's code + */ + static std::map languages + { + {"afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale}}, + {"armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale}}, + {"chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale}}, + {"english", {"English", "en", i2p::i18n::english::GetLocale}}, + {"french", {"Français", "fr", i2p::i18n::french::GetLocale}}, + {"german", {"Deutsch", "de", i2p::i18n::german::GetLocale}}, + {"russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale}}, + {"turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale}}, + {"ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale}}, + {"uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale}}, + }; -} // i18n + } // i18n } // i2p #endif // __I18N_LANGS_H__ diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp index d7616e9e..822fa7e6 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -14,202 +14,201 @@ // Russian localization file -namespace i2p -{ -namespace i18n -{ -namespace russian // language namespace -{ - // language name in lowercase - static std::string language = "russian"; +namespace i2p { + namespace i18n { + namespace russian // language namespace + { + // language name in lowercase + static std::string language = "russian"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - 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; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + 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; + } - static std::map strings - { - {"KiB", "КиБ"}, - {"MiB", "МиБ"}, - {"GiB", "ГиБ"}, - {"building", "строится"}, - {"failed", "неудачный"}, - {"expiring", "истекает"}, - {"established", "работает"}, - {"unknown", "неизвестно"}, - {"exploratory", "исследовательский"}, - {"i2pd webconsole", "Веб-консоль i2pd"}, - {"Main page", "Главная"}, - {"Router commands", "Команды роутера"}, - {"Local Destinations", "Локальные назначения"}, - {"LeaseSets", "Лизсеты"}, - {"Tunnels", "Туннели"}, - {"Transit Tunnels", "Транзитные туннели"}, - {"Transports", "Транспорты"}, - {"I2P tunnels", "I2P туннели"}, - {"SAM sessions", "SAM сессии"}, - {"ERROR", "ОШИБКА"}, - {"OK", "OK"}, - {"Testing", "Тестирование"}, - {"Firewalled", "Заблокировано извне"}, - {"Unknown", "Неизвестно"}, - {"Proxy", "Прокси"}, - {"Mesh", "MESH-сеть"}, - {"Error", "Ошибка"}, - {"Clock skew", "Не точное время"}, - {"Offline", "Оффлайн"}, - {"Symmetric NAT", "Симметричный NAT"}, - {"Uptime", "В сети"}, - {"Network status", "Сетевой статус"}, - {"Network status v6", "Сетевой статус v6"}, - {"Stopping in", "Остановка через"}, - {"Family", "Семейство"}, - {"Tunnel creation success rate", "Успешно построенных туннелей"}, - {"Received", "Получено"}, - {"KiB/s", "КиБ/с"}, - {"Sent", "Отправлено"}, - {"Transit", "Транзит"}, - {"Data path", "Путь к данным"}, - {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."}, - {"Router Ident", "Идентификатор роутера"}, - {"Router Family", "Семейство роутера"}, - {"Router Caps", "Флаги роутера"}, - {"Version", "Версия"}, - {"Our external address", "Наш внешний адрес"}, - {"supported", "поддерживается"}, - {"Routers", "Роутеры"}, - {"Floodfills", "Флудфилы"}, - {"Client Tunnels", "Клиентские туннели"}, - {"Services", "Сервисы"}, - {"Enabled", "Включено"}, - {"Disabled", "Выключено"}, - {"Encrypted B33 address", "Шифрованные B33 адреса"}, - {"Address registration line", "Строка регистрации адреса"}, - {"Domain", "Домен"}, - {"Generate", "Сгенерировать"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примечание: полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."}, - {"Address", "Адрес"}, - {"Type", "Тип"}, - {"EncType", "ТипШифр"}, - {"Inbound tunnels", "Входящие туннели"}, - {"ms", "мс"}, - {"Outbound tunnels", "Исходящие туннели"}, - {"Tags", "Теги"}, - {"Incoming", "Входящие"}, - {"Outgoing", "Исходящие"}, - {"Destination", "Назначение"}, - {"Amount", "Количество"}, - {"Incoming Tags", "Входящие теги"}, - {"Tags sessions", "Сессии тегов"}, - {"Status", "Статус"}, - {"Local Destination", "Локальное назначение"}, - {"Streams", "Стримы"}, - {"Close stream", "Закрыть стрим"}, - {"I2CP session not found", "I2CP сессия не найдена"}, - {"I2CP is not enabled", "I2CP не включен"}, - {"Invalid", "Некорректный"}, - {"Store type", "Тип хранилища"}, - {"Expires", "Истекает"}, - {"Non Expired Leases", "Не истекшие Lease-ы"}, - {"Gateway", "Шлюз"}, - {"TunnelID", "ID туннеля"}, - {"EndDate", "Заканчивается"}, - {"not floodfill", "не флудфил"}, - {"Queue size", "Размер очереди"}, - {"Run peer test", "Запустить тестирование"}, - {"Decline transit tunnels", "Отклонять транзитные туннели"}, - {"Accept transit tunnels", "Принимать транзитные туннели"}, - {"Cancel graceful shutdown", "Отменить плавную остановку"}, - {"Start graceful shutdown", "Запустить плавную остановку"}, - {"Force shutdown", "Принудительная остановка"}, - {"Reload external CSS styles", "Перезагрузить внешние CSS стили"}, - {"Note: any action done here are not persistent and not changes your config files.", "Примечание: любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, - {"Logging level", "Уровень логирования"}, - {"Transit tunnels limit", "Лимит транзитных туннелей"}, - {"Change", "Изменить"}, - {"Change language", "Изменение языка"}, - {"no transit tunnels currently built", "нет построенных транзитных туннелей"}, - {"SAM disabled", "SAM выключен"}, - {"no sessions currently running", "нет запущенных сессий"}, - {"SAM session not found", "SAM сессия не найдена"}, - {"SAM Session", "SAM сессия"}, - {"Server Tunnels", "Серверные туннели"}, - {"Client Forwards", "Клиентские перенаправления"}, - {"Server Forwards", "Серверные перенаправления"}, - {"Unknown page", "Неизвестная страница"}, - {"Invalid token", "Неверный токен"}, - {"SUCCESS", "УСПЕШНО"}, - {"Stream closed", "Стрим закрыт"}, - {"Stream not found or already was closed", "Стрим не найден или уже закрыт"}, - {"Destination not found", "Точка назначения не найдена"}, - {"StreamID can't be null", "StreamID не может быть пустым"}, - {"Return to destination page", "Вернуться на страницу точки назначения"}, - {"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"}, - {"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"}, - {"Back to commands list", "Вернуться к списку команд"}, - {"Register at reg.i2p", "Зарегистрировать на reg.i2p"}, - {"Description", "Описание"}, - {"A bit information about service on domain", "Немного информации о сервисе на домене"}, - {"Submit", "Отправить"}, - {"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"}, - {"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"}, - {"Such destination is not found", "Такая точка назначения не найдена"}, - {"Unknown command", "Неизвестная команда"}, - {"Command accepted", "Команда принята"}, - {"Proxy error", "Ошибка прокси"}, - {"Proxy info", "Информация прокси"}, - {"Proxy error: Host not found", "Ошибка прокси: Узел не найден"}, - {"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"}, - {"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"}, - {"Invalid request", "Некорректный запрос"}, - {"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"}, - {"addresshelper is not supported", "addresshelper не поддерживается"}, - {"Host", "Узел"}, - {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, - {"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"}, - {"Continue", "Продолжить"}, - {"Addresshelper found", "Найден addresshelper"}, - {"already in router's addressbook", "уже в адресной книге роутера"}, - {"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"}, - {"invalid request uri", "некорректный URI запроса"}, - {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"}, - {"Outproxy failure", "Ошибка внешнего прокси"}, - {"bad outproxy settings", "некорректные настройки внешнего прокси"}, - {"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"}, - {"unknown outproxy url", "неизвестный URL внешнего прокси"}, - {"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"}, - {"hostname too long", "имя хоста слишком длинное"}, - {"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"}, - {"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"}, - {"CONNECT error", "Ошибка CONNECT запроса"}, - {"Failed to Connect", "Не удалось подключиться"}, - {"socks proxy error", "ошибка SOCKS прокси"}, - {"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"}, - {"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"}, - {"cannot connect", "не удалось подключиться"}, - {"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"}, - {"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"}, - {"Host is down", "Узел недоступен"}, - {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "КиБ"}, + {"MiB", "МиБ"}, + {"GiB", "ГиБ"}, + {"building", "строится"}, + {"failed", "неудачный"}, + {"expiring", "истекает"}, + {"established", "работает"}, + {"unknown", "неизвестно"}, + {"exploratory", "исследовательский"}, + {"i2pd webconsole", "Веб-консоль i2pd"}, + {"Main page", "Главная"}, + {"Router commands", "Команды роутера"}, + {"Local Destinations", "Локальные назначения"}, + {"LeaseSets", "Лизсеты"}, + {"Tunnels", "Туннели"}, + {"Transit Tunnels", "Транзитные туннели"}, + {"Transports", "Транспорты"}, + {"I2P tunnels", "I2P туннели"}, + {"SAM sessions", "SAM сессии"}, + {"ERROR", "ОШИБКА"}, + {"OK", "OK"}, + {"Testing", "Тестирование"}, + {"Firewalled", "Заблокировано извне"}, + {"Unknown", "Неизвестно"}, + {"Proxy", "Прокси"}, + {"Mesh", "MESH-сеть"}, + {"Error", "Ошибка"}, + {"Clock skew", "Не точное время"}, + {"Offline", "Оффлайн"}, + {"Symmetric NAT", "Симметричный NAT"}, + {"Uptime", "В сети"}, + {"Network status", "Сетевой статус"}, + {"Network status v6", "Сетевой статус v6"}, + {"Stopping in", "Остановка через"}, + {"Family", "Семейство"}, + {"Tunnel creation success rate", "Успешно построенных туннелей"}, + {"Received", "Получено"}, + {"KiB/s", "КиБ/с"}, + {"Sent", "Отправлено"}, + {"Transit", "Транзит"}, + {"Data path", "Путь к данным"}, + {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."}, + {"Router Ident", "Идентификатор роутера"}, + {"Router Family", "Семейство роутера"}, + {"Router Caps", "Флаги роутера"}, + {"Version", "Версия"}, + {"Our external address", "Наш внешний адрес"}, + {"supported", "поддерживается"}, + {"Routers", "Роутеры"}, + {"Floodfills", "Флудфилы"}, + {"Client Tunnels", "Клиентские туннели"}, + {"Services", "Сервисы"}, + {"Enabled", "Включено"}, + {"Disabled", "Выключено"}, + {"Encrypted B33 address", "Шифрованные B33 адреса"}, + {"Address registration line", "Строка регистрации адреса"}, + {"Domain", "Домен"}, + {"Generate", "Сгенерировать"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примечание: полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."}, + {"Address", "Адрес"}, + {"Type", "Тип"}, + {"EncType", "ТипШифр"}, + {"Inbound tunnels", "Входящие туннели"}, + {"ms", "мс"}, + {"Outbound tunnels", "Исходящие туннели"}, + {"Tags", "Теги"}, + {"Incoming", "Входящие"}, + {"Outgoing", "Исходящие"}, + {"Destination", "Назначение"}, + {"Amount", "Количество"}, + {"Incoming Tags", "Входящие теги"}, + {"Tags sessions", "Сессии тегов"}, + {"Status", "Статус"}, + {"Local Destination", "Локальное назначение"}, + {"Streams", "Стримы"}, + {"Close stream", "Закрыть стрим"}, + {"I2CP session not found", "I2CP сессия не найдена"}, + {"I2CP is not enabled", "I2CP не включен"}, + {"Invalid", "Некорректный"}, + {"Store type", "Тип хранилища"}, + {"Expires", "Истекает"}, + {"Non Expired Leases", "Не истекшие Lease-ы"}, + {"Gateway", "Шлюз"}, + {"TunnelID", "ID туннеля"}, + {"EndDate", "Заканчивается"}, + {"not floodfill", "не флудфил"}, + {"Queue size", "Размер очереди"}, + {"Run peer test", "Запустить тестирование"}, + {"Decline transit tunnels", "Отклонять транзитные туннели"}, + {"Accept transit tunnels", "Принимать транзитные туннели"}, + {"Cancel graceful shutdown", "Отменить плавную остановку"}, + {"Start graceful shutdown", "Запустить плавную остановку"}, + {"Force shutdown", "Принудительная остановка"}, + {"Reload external CSS styles", "Перезагрузить внешние CSS стили"}, + {"Note: any action done here are not persistent and not changes your config files.", "Примечание: любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, + {"Logging level", "Уровень логирования"}, + {"Transit tunnels limit", "Лимит транзитных туннелей"}, + {"Change", "Изменить"}, + {"Change language", "Изменение языка"}, + {"no transit tunnels currently built", "нет построенных транзитных туннелей"}, + {"SAM disabled", "SAM выключен"}, + {"no sessions currently running", "нет запущенных сессий"}, + {"SAM session not found", "SAM сессия не найдена"}, + {"SAM Session", "SAM сессия"}, + {"Server Tunnels", "Серверные туннели"}, + {"Client Forwards", "Клиентские перенаправления"}, + {"Server Forwards", "Серверные перенаправления"}, + {"Unknown page", "Неизвестная страница"}, + {"Invalid token", "Неверный токен"}, + {"SUCCESS", "УСПЕШНО"}, + {"Stream closed", "Стрим закрыт"}, + {"Stream not found or already was closed", "Стрим не найден или уже закрыт"}, + {"Destination not found", "Точка назначения не найдена"}, + {"StreamID can't be null", "StreamID не может быть пустым"}, + {"Return to destination page", "Вернуться на страницу точки назначения"}, + {"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"}, + {"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"}, + {"Back to commands list", "Вернуться к списку команд"}, + {"Register at reg.i2p", "Зарегистрировать на reg.i2p"}, + {"Description", "Описание"}, + {"A bit information about service on domain", "Немного информации о сервисе на домене"}, + {"Submit", "Отправить"}, + {"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"}, + {"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"}, + {"Such destination is not found", "Такая точка назначения не найдена"}, + {"Unknown command", "Неизвестная команда"}, + {"Command accepted", "Команда принята"}, + {"Proxy error", "Ошибка прокси"}, + {"Proxy info", "Информация прокси"}, + {"Proxy error: Host not found", "Ошибка прокси: Узел не найден"}, + {"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"}, + {"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"}, + {"Invalid request", "Некорректный запрос"}, + {"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"}, + {"addresshelper is not supported", "addresshelper не поддерживается"}, + {"Host", "Узел"}, + {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, + {"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"}, + {"Continue", "Продолжить"}, + {"Addresshelper found", "Найден addresshelper"}, + {"already in router's addressbook", "уже в адресной книге роутера"}, + {"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"}, + {"invalid request uri", "некорректный URI запроса"}, + {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"}, + {"Outproxy failure", "Ошибка внешнего прокси"}, + {"bad outproxy settings", "некорректные настройки внешнего прокси"}, + {"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"}, + {"unknown outproxy url", "неизвестный URL внешнего прокси"}, + {"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"}, + {"hostname too long", "имя хоста слишком длинное"}, + {"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"}, + {"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"}, + {"CONNECT error", "Ошибка CONNECT запроса"}, + {"Failed to Connect", "Не удалось подключиться"}, + {"socks proxy error", "ошибка SOCKS прокси"}, + {"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"}, + {"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"}, + {"cannot connect", "не удалось подключиться"}, + {"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"}, + {"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"}, + {"Host is down", "Узел недоступен"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"день", "дня", "дней"}}, - {"hours", {"час", "часа", "часов"}}, - {"minutes", {"минуту", "минуты", "минут"}}, - {"seconds", {"секунду", "секунды", "секунд"}}, - {"", {"", "", ""}}, - }; + static std::map > plurals + { + {"days", {"день", "дня", "дней"}}, + {"hours", {"час", "часа", "часов"}}, + {"minutes", {"минуту", "минуты", "минут"}}, + {"seconds", {"секунду", "секунды", "секунд"}}, + {"", {"", "", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/Turkmen.cpp b/i18n/Turkmen.cpp index 356ada85..9f07f6c8 100644 --- a/i18n/Turkmen.cpp +++ b/i18n/Turkmen.cpp @@ -14,202 +14,200 @@ // Turkmen localization file -namespace i2p -{ -namespace i18n -{ -namespace turkmen // language namespace -{ - // language name in lowercase - static std::string language = "turkmen"; +namespace i2p { + namespace i18n { + namespace turkmen // language namespace + { + // language name in lowercase + static std::string language = "turkmen"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n != 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n != 1 ? 1 : 0; + } - static std::map strings - { - {"KiB", "KiB"}, - {"MiB", "MiB"}, - {"GiB", "GiB"}, - {"building", "bina"}, - {"failed", "şowsuz"}, - {"expiring", "möhleti gutarýar"}, - {"established", "işleýär"}, - {"unknown", "näbelli"}, - {"exploratory", "gözleg"}, - {"i2pd webconsole", "Web konsoly i2pd"}, - {"Main page", "Esasy sahypa"}, - {"Router commands", "Marşrutizator buýruklary"}, - {"Local Destinations", "Ýerli ýerler"}, - {"LeaseSets", "Lizset"}, - {"Tunnels", "Tuneller"}, - {"Transit Tunnels", "Tranzit Tunelleri"}, - {"Transports", "Daşamak"}, - {"I2P tunnels", "I2P tuneller"}, - {"SAM sessions", "SAM Sessiýasy"}, - {"ERROR", "Ýalňyşlyk"}, - {"OK", "OK"}, - {"Testing", "Synag etmek"}, - {"Firewalled", "Daşynda petiklendi"}, - {"Unknown", "Näbelli"}, - {"Proxy", "Proksi"}, - {"Mesh", "MESH-tor"}, - {"Error", "Ýalňyşlyk"}, - {"Clock skew", "Takyk wagt däl"}, - {"Offline", "Awtonom"}, - {"Symmetric NAT", "Simmetriklik NAT"}, - {"Uptime", "Onlaýn onlaýn sözlügi"}, - {"Network status", "Tor ýagdaýy"}, - {"Network status v6", "Tor ýagdaýy v6"}, - {"Stopping in", "Soň duruň"}, - {"Family", "Maşgala"}, - {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, - {"Received", "Alnan"}, - {"KiB/s", "KiB/s"}, - {"Sent", "Ýerleşdirildi"}, - {"Transit", "Tranzit"}, - {"Data path", "Maglumat ýoly"}, - {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."}, - {"Router Ident", "Marşrutly kesgitleýji"}, - {"Router Family", "Marşrutler maşgalasy"}, - {"Router Caps", "Baýdaklar marşruteri"}, - {"Version", "Wersiýasy"}, - {"Our external address", "Daşarky salgymyz"}, - {"supported", "goldanýar"}, - {"Routers", "Marşrutizatorlar"}, - {"Floodfills", "Fludfillar"}, - {"Client Tunnels", "Müşderi tunelleri"}, - {"Services", "Hyzmatlar"}, - {"Enabled", "Goşuldy"}, - {"Disabled", "Öçürildi"}, - {"Encrypted B33 address", "Şifrlenen B33 salgylar"}, - {"Address registration line", "Hasaba alyş salgysy"}, - {"Domain", "Domen"}, - {"Generate", "Öndürmek"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Bellik: Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner (example.i2p). Subýutmalary hasaba almak üçin i2pd ulanyň-tools."}, - {"Address", "Salgysy"}, - {"Type", "Görnüş"}, - {"EncType", "Şifrlemek görnüşi"}, - {"Inbound tunnels", "Gelýän tuneller"}, - {"ms", "ms"}, - {"Outbound tunnels", "Çykýan tuneller"}, - {"Tags", "Bellikler"}, - {"Incoming", "Gelýän"}, - {"Outgoing", "Çykýan"}, - {"Destination", "Maksat"}, - {"Amount", "Sany"}, - {"Incoming Tags", "Gelýän bellikler"}, - {"Tags sessions", "Sapaklar bellikler"}, - {"Status", "Ýagdaýy"}, - {"Local Destination", "Ýerli maksat"}, - {"Streams", "Strimlary"}, - {"Close stream", "Yap strim"}, - {"I2CP session not found", "I2CP Sessiýa tapylmady"}, - {"I2CP is not enabled", "I2CP goşulmaýar"}, - {"Invalid", "Nädogry"}, - {"Store type", "Ammar görnüşi"}, - {"Expires", "Möhleti gutarýar"}, - {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"}, - {"Gateway", "Derweze"}, - {"TunnelID", "Tuneliň ID"}, - {"EndDate", "Gutarýar"}, - {"not floodfill", "fludfil däl"}, - {"Queue size", "Nobatyň ululygy"}, - {"Run peer test", "Synag başlaň"}, - {"Decline transit tunnels", "Tranzit tunellerini ret ediň"}, - {"Accept transit tunnels", "Tranzit tunellerini alyň"}, - {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"}, - {"Start graceful shutdown", "Tekiz durmak"}, - {"Force shutdown", "Mejbury duralga"}, - {"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"}, - {"Note: any action done here are not persistent and not changes your config files.", "Bellik: Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."}, - {"Logging level", "Giriş derejesi"}, - {"Transit tunnels limit", "Tranzit tunelleriniň çägi"}, - {"Change", "Üýtgetmek"}, - {"Change language", "Dil üýtgetmek"}, - {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"}, - {"SAM disabled", "SAM öçürilen"}, - {"no sessions currently running", "başlamagyň sessiýalary ýok"}, - {"SAM session not found", "SAM Sessiýa tapylmady"}, - {"SAM Session", "SAM Sessiýa"}, - {"Server Tunnels", "Serwer tunelleri"}, - {"Client Forwards", "Müşderi gönükdirýär"}, - {"Server Forwards", "Serweriň täzeden düzlüleri"}, - {"Unknown page", "Näbelli sahypa"}, - {"Invalid token", "Nädogry token"}, - {"SUCCESS", "Üstünlikli"}, - {"Stream closed", "Strim ýapyk"}, - {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"}, - {"Destination not found", "Niýetlenen ýeri tapylmady"}, - {"StreamID can't be null", "StreamID boş bolup bilmez"}, - {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"}, - {"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"}, - {"Back to commands list", "Topar sanawyna dolan"}, - {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"}, - {"Description", "Beýany"}, - {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"}, - {"Submit", "Iber"}, - {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"}, - {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"}, - {"Such destination is not found", "Bu barmaly ýer tapylmady"}, - {"Unknown command", "Näbelli topar"}, - {"Command accepted", "Topar kabul edilýär"}, - {"Proxy error", "Proksi ýalňyşlygy"}, - {"Proxy info", "Proksi maglumat"}, - {"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"}, - {"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ş"}, - {"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"}, - {"addresshelper is not supported", "Salgylandyryjy goldanok"}, - {"Host", "Adres"}, - {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"}, - {"Click here to proceed:", "Dowam etmek bu ýerde basyň:"}, - {"Continue", "Dowam et"}, - {"Addresshelper found", "Forgelper tapyldy"}, - {"already in router's addressbook", "marşruteriň adres kitaby"}, - {"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"}, - {"invalid request uri", "nädogry haýyş URI"}, - {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"}, - {"Outproxy failure", "Daşarky proksi ýalňyşlyk"}, - {"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"}, - {"unknown outproxy url", "näbelli daşarky proksi URL"}, - {"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"}, - {"hostname too long", "hoster eýesi ady gaty uzyn"}, - {"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"}, - {"CONNECT error", "Bagyr haýyşy säwligi"}, - {"Failed to Connect", "Birikdirip bilmedi"}, - {"socks proxy error", "socks proksi ýalňyşlygy"}, - {"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"}, - {"cannot connect", "birikdirip bilmedi"}, - {"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"}, - {"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ň."}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "bina"}, + {"failed", "şowsuz"}, + {"expiring", "möhleti gutarýar"}, + {"established", "işleýär"}, + {"unknown", "näbelli"}, + {"exploratory", "gözleg"}, + {"i2pd webconsole", "Web konsoly i2pd"}, + {"Main page", "Esasy sahypa"}, + {"Router commands", "Marşrutizator buýruklary"}, + {"Local Destinations", "Ýerli ýerler"}, + {"LeaseSets", "Lizset"}, + {"Tunnels", "Tuneller"}, + {"Transit Tunnels", "Tranzit Tunelleri"}, + {"Transports", "Daşamak"}, + {"I2P tunnels", "I2P tuneller"}, + {"SAM sessions", "SAM Sessiýasy"}, + {"ERROR", "Ýalňyşlyk"}, + {"OK", "OK"}, + {"Testing", "Synag etmek"}, + {"Firewalled", "Daşynda petiklendi"}, + {"Unknown", "Näbelli"}, + {"Proxy", "Proksi"}, + {"Mesh", "MESH-tor"}, + {"Error", "Ýalňyşlyk"}, + {"Clock skew", "Takyk wagt däl"}, + {"Offline", "Awtonom"}, + {"Symmetric NAT", "Simmetriklik NAT"}, + {"Uptime", "Onlaýn onlaýn sözlügi"}, + {"Network status", "Tor ýagdaýy"}, + {"Network status v6", "Tor ýagdaýy v6"}, + {"Stopping in", "Soň duruň"}, + {"Family", "Maşgala"}, + {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, + {"Received", "Alnan"}, + {"KiB/s", "KiB/s"}, + {"Sent", "Ýerleşdirildi"}, + {"Transit", "Tranzit"}, + {"Data path", "Maglumat ýoly"}, + {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."}, + {"Router Ident", "Marşrutly kesgitleýji"}, + {"Router Family", "Marşrutler maşgalasy"}, + {"Router Caps", "Baýdaklar marşruteri"}, + {"Version", "Wersiýasy"}, + {"Our external address", "Daşarky salgymyz"}, + {"supported", "goldanýar"}, + {"Routers", "Marşrutizatorlar"}, + {"Floodfills", "Fludfillar"}, + {"Client Tunnels", "Müşderi tunelleri"}, + {"Services", "Hyzmatlar"}, + {"Enabled", "Goşuldy"}, + {"Disabled", "Öçürildi"}, + {"Encrypted B33 address", "Şifrlenen B33 salgylar"}, + {"Address registration line", "Hasaba alyş salgysy"}, + {"Domain", "Domen"}, + {"Generate", "Öndürmek"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Bellik: Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner (example.i2p). Subýutmalary hasaba almak üçin i2pd ulanyň-tools."}, + {"Address", "Salgysy"}, + {"Type", "Görnüş"}, + {"EncType", "Şifrlemek görnüşi"}, + {"Inbound tunnels", "Gelýän tuneller"}, + {"ms", "ms"}, + {"Outbound tunnels", "Çykýan tuneller"}, + {"Tags", "Bellikler"}, + {"Incoming", "Gelýän"}, + {"Outgoing", "Çykýan"}, + {"Destination", "Maksat"}, + {"Amount", "Sany"}, + {"Incoming Tags", "Gelýän bellikler"}, + {"Tags sessions", "Sapaklar bellikler"}, + {"Status", "Ýagdaýy"}, + {"Local Destination", "Ýerli maksat"}, + {"Streams", "Strimlary"}, + {"Close stream", "Yap strim"}, + {"I2CP session not found", "I2CP Sessiýa tapylmady"}, + {"I2CP is not enabled", "I2CP goşulmaýar"}, + {"Invalid", "Nädogry"}, + {"Store type", "Ammar görnüşi"}, + {"Expires", "Möhleti gutarýar"}, + {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"}, + {"Gateway", "Derweze"}, + {"TunnelID", "Tuneliň ID"}, + {"EndDate", "Gutarýar"}, + {"not floodfill", "fludfil däl"}, + {"Queue size", "Nobatyň ululygy"}, + {"Run peer test", "Synag başlaň"}, + {"Decline transit tunnels", "Tranzit tunellerini ret ediň"}, + {"Accept transit tunnels", "Tranzit tunellerini alyň"}, + {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"}, + {"Start graceful shutdown", "Tekiz durmak"}, + {"Force shutdown", "Mejbury duralga"}, + {"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"}, + {"Note: any action done here are not persistent and not changes your config files.", "Bellik: Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."}, + {"Logging level", "Giriş derejesi"}, + {"Transit tunnels limit", "Tranzit tunelleriniň çägi"}, + {"Change", "Üýtgetmek"}, + {"Change language", "Dil üýtgetmek"}, + {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"}, + {"SAM disabled", "SAM öçürilen"}, + {"no sessions currently running", "başlamagyň sessiýalary ýok"}, + {"SAM session not found", "SAM Sessiýa tapylmady"}, + {"SAM Session", "SAM Sessiýa"}, + {"Server Tunnels", "Serwer tunelleri"}, + {"Client Forwards", "Müşderi gönükdirýär"}, + {"Server Forwards", "Serweriň täzeden düzlüleri"}, + {"Unknown page", "Näbelli sahypa"}, + {"Invalid token", "Nädogry token"}, + {"SUCCESS", "Üstünlikli"}, + {"Stream closed", "Strim ýapyk"}, + {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"}, + {"Destination not found", "Niýetlenen ýeri tapylmady"}, + {"StreamID can't be null", "StreamID boş bolup bilmez"}, + {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"}, + {"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"}, + {"Back to commands list", "Topar sanawyna dolan"}, + {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"}, + {"Description", "Beýany"}, + {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"}, + {"Submit", "Iber"}, + {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"}, + {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"}, + {"Such destination is not found", "Bu barmaly ýer tapylmady"}, + {"Unknown command", "Näbelli topar"}, + {"Command accepted", "Topar kabul edilýär"}, + {"Proxy error", "Proksi ýalňyşlygy"}, + {"Proxy info", "Proksi maglumat"}, + {"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"}, + {"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ş"}, + {"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"}, + {"addresshelper is not supported", "Salgylandyryjy goldanok"}, + {"Host", "Adres"}, + {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"}, + {"Click here to proceed:", "Dowam etmek bu ýerde basyň:"}, + {"Continue", "Dowam et"}, + {"Addresshelper found", "Forgelper tapyldy"}, + {"already in router's addressbook", "marşruteriň adres kitaby"}, + {"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"}, + {"invalid request uri", "nädogry haýyş URI"}, + {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"}, + {"Outproxy failure", "Daşarky proksi ýalňyşlyk"}, + {"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"}, + {"unknown outproxy url", "näbelli daşarky proksi URL"}, + {"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"}, + {"hostname too long", "hoster eýesi ady gaty uzyn"}, + {"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"}, + {"CONNECT error", "Bagyr haýyşy säwligi"}, + {"Failed to Connect", "Birikdirip bilmedi"}, + {"socks proxy error", "socks proksi ýalňyşlygy"}, + {"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"}, + {"cannot connect", "birikdirip bilmedi"}, + {"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"}, + {"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ň."}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"gün", "gün"}}, - {"hours", {"sagat", "sagat"}}, - {"minutes", {"minut", "minut"}}, - {"seconds", {"sekunt", "sekunt"}}, - {"", {"", ""}}, - }; + static std::map > plurals + { + {"days", {"gün", "gün"}}, + {"hours", {"sagat", "sagat"}}, + {"minutes", {"minut", "minut"}}, + {"seconds", {"sekunt", "sekunt"}}, + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/Ukrainian.cpp b/i18n/Ukrainian.cpp index abbe8f81..3290b665 100644 --- a/i18n/Ukrainian.cpp +++ b/i18n/Ukrainian.cpp @@ -14,202 +14,201 @@ // Ukrainian localization file -namespace i2p -{ -namespace i18n -{ -namespace ukrainian // language namespace -{ - // language name in lowercase - static std::string language = "ukrainian"; +namespace i2p { + namespace i18n { + namespace ukrainian // language namespace + { + // language name in lowercase + static std::string language = "ukrainian"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - 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; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + 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; + } - static std::map strings - { - {"KiB", "КіБ"}, - {"MiB", "МіБ"}, - {"GiB", "ГіБ"}, - {"building", "будується"}, - {"failed", "невдалий"}, - {"expiring", "завершується"}, - {"established", "працює"}, - {"unknown", "невідомо"}, - {"exploratory", "дослідницький"}, - {"i2pd webconsole", "Веб-консоль i2pd"}, - {"Main page", "Головна"}, - {"Router commands", "Команди маршрутизатора"}, - {"Local Destinations", "Локальні Призначення"}, - {"LeaseSets", "Лізсети"}, - {"Tunnels", "Тунелі"}, - {"Transit Tunnels", "Транзитні Тунелі"}, - {"Transports", "Транспорти"}, - {"I2P tunnels", "I2P тунелі"}, - {"SAM sessions", "SAM сесії"}, - {"ERROR", "ПОМИЛКА"}, - {"OK", "OK"}, - {"Testing", "Тестування"}, - {"Firewalled", "Заблоковано ззовні"}, - {"Unknown", "Невідомо"}, - {"Proxy", "Проксі"}, - {"Mesh", "MESH-мережа"}, - {"Error", "Помилка"}, - {"Clock skew", "Неточний час"}, - {"Offline", "Офлайн"}, - {"Symmetric NAT", "Симетричний NAT"}, - {"Uptime", "У мережі"}, - {"Network status", "Мережевий статус"}, - {"Network status v6", "Мережевий статус v6"}, - {"Stopping in", "Зупинка через"}, - {"Family", "Сімейство"}, - {"Tunnel creation success rate", "Успішно побудованих тунелів"}, - {"Received", "Отримано"}, - {"KiB/s", "КіБ/с"}, - {"Sent", "Відправлено"}, - {"Transit", "Транзит"}, - {"Data path", "Шлях до даних"}, - {"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."}, - {"Router Ident", "Ідентифікатор маршрутизатора"}, - {"Router Family", "Сімейство маршрутизатора"}, - {"Router Caps", "Прапорці маршрутизатора"}, - {"Version", "Версія"}, - {"Our external address", "Наша зовнішня адреса"}, - {"supported", "підтримується"}, - {"Routers", "Маршрутизатори"}, - {"Floodfills", "Флудфіли"}, - {"Client Tunnels", "Клієнтські Тунелі"}, - {"Services", "Сервіси"}, - {"Enabled", "Увімкнуто"}, - {"Disabled", "Вимкнуто"}, - {"Encrypted B33 address", "Шифровані B33 адреси"}, - {"Address registration line", "Рядок реєстрації адреси"}, - {"Domain", "Домен"}, - {"Generate", "Згенерувати"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примітка: отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."}, - {"Address", "Адреса"}, - {"Type", "Тип"}, - {"EncType", "ТипШифр"}, - {"Inbound tunnels", "Вхідні тунелі"}, - {"ms", "мс"}, - {"Outbound tunnels", "Вихідні тунелі"}, - {"Tags", "Теги"}, - {"Incoming", "Вхідні"}, - {"Outgoing", "Вихідні"}, - {"Destination", "Призначення"}, - {"Amount", "Кількість"}, - {"Incoming Tags", "Вхідні Теги"}, - {"Tags sessions", "Сесії Тегів"}, - {"Status", "Статус"}, - {"Local Destination", "Локальні Призначення"}, - {"Streams", "Потоки"}, - {"Close stream", "Закрити потік"}, - {"I2CP session not found", "I2CP сесія не знайдена"}, - {"I2CP is not enabled", "I2CP не увікнуто"}, - {"Invalid", "Некоректний"}, - {"Store type", "Тип сховища"}, - {"Expires", "Завершується"}, - {"Non Expired Leases", "Не завершені Lease-и"}, - {"Gateway", "Шлюз"}, - {"TunnelID", "ID тунеля"}, - {"EndDate", "Закінчується"}, - {"not floodfill", "не флудфіл"}, - {"Queue size", "Розмір черги"}, - {"Run peer test", "Запустити тестування"}, - {"Decline transit tunnels", "Відхиляти транзитні тунелі"}, - {"Accept transit tunnels", "Ухвалювати транзитні тунелі"}, - {"Cancel graceful shutdown", "Скасувати плавну зупинку"}, - {"Start graceful shutdown", "Запустити плавну зупинку"}, - {"Force shutdown", "Примусова зупинка"}, - {"Reload external CSS styles", "Перезавантажити зовнішні стилі CSS"}, - {"Note: any action done here are not persistent and not changes your config files.", "Примітка: будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, - {"Logging level", "Рівень логування"}, - {"Transit tunnels limit", "Обмеження транзитних тунелів"}, - {"Change", "Змінити"}, - {"Change language", "Змінити мову"}, - {"no transit tunnels currently built", "немає побудованих транзитних тунелів"}, - {"SAM disabled", "SAM вимкнуто"}, - {"no sessions currently running", "немає запущених сесій"}, - {"SAM session not found", "SAM сесія не знайдена"}, - {"SAM Session", "SAM сесія"}, - {"Server Tunnels", "Серверні Тунелі"}, - {"Client Forwards", "Клієнтські Переспрямування"}, - {"Server Forwards", "Серверні Переспрямування"}, - {"Unknown page", "Невідома сторінка"}, - {"Invalid token", "Невірний токен"}, - {"SUCCESS", "УСПІШНО"}, - {"Stream closed", "Потік зачинений"}, - {"Stream not found or already was closed", "Потік не знайдений або вже зачинений"}, - {"Destination not found", "Точка призначення не знайдена"}, - {"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"}, - {"Return to destination page", "Повернутися на сторінку точки призначення"}, - {"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"}, - {"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"}, - {"Back to commands list", "Повернутися до списку команд"}, - {"Register at reg.i2p", "Зареєструвати на reg.i2p"}, - {"Description", "Опис"}, - {"A bit information about service on domain", "Трохи інформації про сервіс на домені"}, - {"Submit", "Надіслати"}, - {"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"}, - {"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"}, - {"Such destination is not found", "Така точка призначення не знайдена"}, - {"Unknown command", "Невідома команда"}, - {"Command accepted", "Команда прийнята"}, - {"Proxy error", "Помилка проксі"}, - {"Proxy info", "Інформація проксі"}, - {"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"}, - {"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"}, - {"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"}, - {"Invalid request", "Некоректний запит"}, - {"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"}, - {"addresshelper is not supported", "addresshelper не підтримується"}, - {"Host", "Адреса"}, - {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"}, - {"Click here to proceed:", "Натисніть тут щоб продовжити:"}, - {"Continue", "Продовжити"}, - {"Addresshelper found", "Знайдено addresshelper"}, - {"already in router's addressbook", "вже в адресній книзі маршрутизатора"}, - {"Click here to update record:", "Натисніть тут щоб оновити запис:"}, - {"invalid request uri", "некоректний URI запиту"}, - {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"}, - {"Outproxy failure", "Помилка зовнішнього проксі"}, - {"bad outproxy settings", "некоректні налаштування зовнішнього проксі"}, - {"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"}, - {"unknown outproxy url", "невідомий URL зовнішнього проксі"}, - {"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"}, - {"hostname too long", "ім'я вузла надто довге"}, - {"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"}, - {"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"}, - {"CONNECT error", "Помилка CONNECT запиту"}, - {"Failed to Connect", "Не вдалося підключитися"}, - {"socks proxy error", "помилка SOCKS проксі"}, - {"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"}, - {"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"}, - {"cannot connect", "не вдалося підключитися"}, - {"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"}, - {"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"}, - {"Host is down", "Вузол недоступний"}, - {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "КіБ"}, + {"MiB", "МіБ"}, + {"GiB", "ГіБ"}, + {"building", "будується"}, + {"failed", "невдалий"}, + {"expiring", "завершується"}, + {"established", "працює"}, + {"unknown", "невідомо"}, + {"exploratory", "дослідницький"}, + {"i2pd webconsole", "Веб-консоль i2pd"}, + {"Main page", "Головна"}, + {"Router commands", "Команди маршрутизатора"}, + {"Local Destinations", "Локальні Призначення"}, + {"LeaseSets", "Лізсети"}, + {"Tunnels", "Тунелі"}, + {"Transit Tunnels", "Транзитні Тунелі"}, + {"Transports", "Транспорти"}, + {"I2P tunnels", "I2P тунелі"}, + {"SAM sessions", "SAM сесії"}, + {"ERROR", "ПОМИЛКА"}, + {"OK", "OK"}, + {"Testing", "Тестування"}, + {"Firewalled", "Заблоковано ззовні"}, + {"Unknown", "Невідомо"}, + {"Proxy", "Проксі"}, + {"Mesh", "MESH-мережа"}, + {"Error", "Помилка"}, + {"Clock skew", "Неточний час"}, + {"Offline", "Офлайн"}, + {"Symmetric NAT", "Симетричний NAT"}, + {"Uptime", "У мережі"}, + {"Network status", "Мережевий статус"}, + {"Network status v6", "Мережевий статус v6"}, + {"Stopping in", "Зупинка через"}, + {"Family", "Сімейство"}, + {"Tunnel creation success rate", "Успішно побудованих тунелів"}, + {"Received", "Отримано"}, + {"KiB/s", "КіБ/с"}, + {"Sent", "Відправлено"}, + {"Transit", "Транзит"}, + {"Data path", "Шлях до даних"}, + {"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."}, + {"Router Ident", "Ідентифікатор маршрутизатора"}, + {"Router Family", "Сімейство маршрутизатора"}, + {"Router Caps", "Прапорці маршрутизатора"}, + {"Version", "Версія"}, + {"Our external address", "Наша зовнішня адреса"}, + {"supported", "підтримується"}, + {"Routers", "Маршрутизатори"}, + {"Floodfills", "Флудфіли"}, + {"Client Tunnels", "Клієнтські Тунелі"}, + {"Services", "Сервіси"}, + {"Enabled", "Увімкнуто"}, + {"Disabled", "Вимкнуто"}, + {"Encrypted B33 address", "Шифровані B33 адреси"}, + {"Address registration line", "Рядок реєстрації адреси"}, + {"Domain", "Домен"}, + {"Generate", "Згенерувати"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примітка: отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."}, + {"Address", "Адреса"}, + {"Type", "Тип"}, + {"EncType", "ТипШифр"}, + {"Inbound tunnels", "Вхідні тунелі"}, + {"ms", "мс"}, + {"Outbound tunnels", "Вихідні тунелі"}, + {"Tags", "Теги"}, + {"Incoming", "Вхідні"}, + {"Outgoing", "Вихідні"}, + {"Destination", "Призначення"}, + {"Amount", "Кількість"}, + {"Incoming Tags", "Вхідні Теги"}, + {"Tags sessions", "Сесії Тегів"}, + {"Status", "Статус"}, + {"Local Destination", "Локальні Призначення"}, + {"Streams", "Потоки"}, + {"Close stream", "Закрити потік"}, + {"I2CP session not found", "I2CP сесія не знайдена"}, + {"I2CP is not enabled", "I2CP не увікнуто"}, + {"Invalid", "Некоректний"}, + {"Store type", "Тип сховища"}, + {"Expires", "Завершується"}, + {"Non Expired Leases", "Не завершені Lease-и"}, + {"Gateway", "Шлюз"}, + {"TunnelID", "ID тунеля"}, + {"EndDate", "Закінчується"}, + {"not floodfill", "не флудфіл"}, + {"Queue size", "Розмір черги"}, + {"Run peer test", "Запустити тестування"}, + {"Decline transit tunnels", "Відхиляти транзитні тунелі"}, + {"Accept transit tunnels", "Ухвалювати транзитні тунелі"}, + {"Cancel graceful shutdown", "Скасувати плавну зупинку"}, + {"Start graceful shutdown", "Запустити плавну зупинку"}, + {"Force shutdown", "Примусова зупинка"}, + {"Reload external CSS styles", "Перезавантажити зовнішні стилі CSS"}, + {"Note: any action done here are not persistent and not changes your config files.", "Примітка: будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, + {"Logging level", "Рівень логування"}, + {"Transit tunnels limit", "Обмеження транзитних тунелів"}, + {"Change", "Змінити"}, + {"Change language", "Змінити мову"}, + {"no transit tunnels currently built", "немає побудованих транзитних тунелів"}, + {"SAM disabled", "SAM вимкнуто"}, + {"no sessions currently running", "немає запущених сесій"}, + {"SAM session not found", "SAM сесія не знайдена"}, + {"SAM Session", "SAM сесія"}, + {"Server Tunnels", "Серверні Тунелі"}, + {"Client Forwards", "Клієнтські Переспрямування"}, + {"Server Forwards", "Серверні Переспрямування"}, + {"Unknown page", "Невідома сторінка"}, + {"Invalid token", "Невірний токен"}, + {"SUCCESS", "УСПІШНО"}, + {"Stream closed", "Потік зачинений"}, + {"Stream not found or already was closed", "Потік не знайдений або вже зачинений"}, + {"Destination not found", "Точка призначення не знайдена"}, + {"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"}, + {"Return to destination page", "Повернутися на сторінку точки призначення"}, + {"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"}, + {"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"}, + {"Back to commands list", "Повернутися до списку команд"}, + {"Register at reg.i2p", "Зареєструвати на reg.i2p"}, + {"Description", "Опис"}, + {"A bit information about service on domain", "Трохи інформації про сервіс на домені"}, + {"Submit", "Надіслати"}, + {"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"}, + {"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"}, + {"Such destination is not found", "Така точка призначення не знайдена"}, + {"Unknown command", "Невідома команда"}, + {"Command accepted", "Команда прийнята"}, + {"Proxy error", "Помилка проксі"}, + {"Proxy info", "Інформація проксі"}, + {"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"}, + {"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"}, + {"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"}, + {"Invalid request", "Некоректний запит"}, + {"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"}, + {"addresshelper is not supported", "addresshelper не підтримується"}, + {"Host", "Адреса"}, + {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"}, + {"Click here to proceed:", "Натисніть тут щоб продовжити:"}, + {"Continue", "Продовжити"}, + {"Addresshelper found", "Знайдено addresshelper"}, + {"already in router's addressbook", "вже в адресній книзі маршрутизатора"}, + {"Click here to update record:", "Натисніть тут щоб оновити запис:"}, + {"invalid request uri", "некоректний URI запиту"}, + {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"}, + {"Outproxy failure", "Помилка зовнішнього проксі"}, + {"bad outproxy settings", "некоректні налаштування зовнішнього проксі"}, + {"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"}, + {"unknown outproxy url", "невідомий URL зовнішнього проксі"}, + {"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"}, + {"hostname too long", "ім'я вузла надто довге"}, + {"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"}, + {"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"}, + {"CONNECT error", "Помилка CONNECT запиту"}, + {"Failed to Connect", "Не вдалося підключитися"}, + {"socks proxy error", "помилка SOCKS проксі"}, + {"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"}, + {"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"}, + {"cannot connect", "не вдалося підключитися"}, + {"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"}, + {"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"}, + {"Host is down", "Вузол недоступний"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"день", "дня", "днів"}}, - {"hours", {"годину", "години", "годин"}}, - {"minutes", {"хвилину", "хвилини", "хвилин"}}, - {"seconds", {"секунду", "секунди", "секунд"}}, - {"", {"", "", ""}}, - }; + static std::map > plurals + { + {"days", {"день", "дня", "днів"}}, + {"hours", {"годину", "години", "годин"}}, + {"minutes", {"хвилину", "хвилини", "хвилин"}}, + {"seconds", {"секунду", "секунди", "секунд"}}, + {"", {"", "", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/i18n/Uzbek.cpp b/i18n/Uzbek.cpp index e750918f..36e9436a 100644 --- a/i18n/Uzbek.cpp +++ b/i18n/Uzbek.cpp @@ -14,202 +14,200 @@ // Ukrainian localization file -namespace i2p -{ -namespace i18n -{ -namespace uzbek // language namespace -{ - // language name in lowercase - static std::string language = "uzbek"; +namespace i2p { + namespace i18n { + namespace uzbek // language namespace + { + // language name in lowercase + static std::string language = "uzbek"; - // See for language plural forms here: - // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - static int plural (int n) { - return n > 1 ? 1 : 0; - } + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural(int n) { + return n > 1 ? 1 : 0; + } - static std::map strings - { - {"KiB", "KiB"}, - {"MiB", "MiB"}, - {"GiB", "GiB"}, - {"building", "yaratilmoqda"}, - {"failed", "muvaffaqiyatsiz"}, - {"expiring", "muddati tugaydi"}, - {"established", "aloqa o'rnatildi"}, - {"unknown", "noma'lum"}, - {"exploratory", "tadqiqiy"}, - {"i2pd webconsole", "i2pd veb-konsoli"}, - {"Main page", "Asosiy sahifa"}, - {"Router commands", "Router buyruqlari"}, - {"Local Destinations", "Mahalliy joylanishlar"}, - {"LeaseSets", "LeaseSets"}, - {"Tunnels", "Tunnellar"}, - {"Transit Tunnels", "Tranzit Tunellari"}, - {"Transports", "Transportlar"}, - {"I2P tunnels", "I2P tunnellari"}, - {"SAM sessions", "SAM sessiyalari"}, - {"ERROR", "XATO"}, - {"OK", "OK"}, - {"Testing", "Testlash"}, - {"Firewalled", "Xavfsizlik devori bilan himoyalangan"}, - {"Unknown", "Notanish"}, - {"Proxy", "Proksi"}, - {"Mesh", "Mesh To'r"}, - {"Error", "Xato"}, - {"Clock skew", "Aniq vaqt emas"}, - {"Offline", "Oflayn"}, - {"Symmetric NAT", "Simmetrik NAT"}, - {"Uptime", "Ish vaqti"}, - {"Network status", "Tarmoq holati"}, - {"Network status v6", "Tarmoq holati v6"}, - {"Stopping in", "Ichida to'xtatish"}, - {"Family", "Oila"}, - {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, - {"Received", "Qabul qilindi"}, - {"KiB/s", "KiB/s"}, - {"Sent", "Yuborilgan"}, - {"Transit", "Tranzit"}, - {"Data path", "Ma'lumotlar joylanishi"}, - {"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."}, - {"Router Ident", "Router identifikatori"}, - {"Router Family", "Router oilasi"}, - {"Router Caps", "Router Bayroqlari"}, - {"Version", "Versiya"}, - {"Our external address", "Bizning tashqi manzilimiz"}, - {"supported", "qo'llab-quvvatlanadi"}, - {"Routers", "Routerlar"}, - {"Floodfills", "Floodfills"}, - {"Client Tunnels", "Mijoz Tunellari"}, - {"Services", "Xizmatlar"}, - {"Enabled", "Yoqilgan"}, - {"Disabled", "O'chirilgan"}, - {"Encrypted B33 address", "Shifrlangan B33 manzil"}, - {"Address registration line", "Manzilni ro'yxatga olish liniyasi"}, - {"Domain", "Domen"}, - {"Generate", "Yaratish"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Eslatma: 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"}, - {"Type", "Turi"}, - {"EncType", "ShifrlashTuri"}, - {"Inbound tunnels", "Kirish tunnellari"}, - {"ms", "ms"}, - {"Outbound tunnels", "Chiquvchi tunnellar"}, - {"Tags", "Teglar"}, - {"Incoming", "Kiruvchi"}, - {"Outgoing", "Chiquvchi"}, - {"Destination", "Manzilgoh"}, - {"Amount", "Soni"}, - {"Incoming Tags", "Kiruvchi teglar"}, - {"Tags sessions", "Teglar sessiyalari"}, - {"Status", "Holat"}, - {"Local Destination", "Mahalliy joylanish"}, - {"Streams", "Strim"}, - {"Close stream", "Strimni o'chirish"}, - {"I2CP session not found", "I2CP sessiyasi topilmadi"}, - {"I2CP is not enabled", "I2CP yoqilmagan"}, - {"Invalid", "Noto'g'ri"}, - {"Store type", "Saqlash turi"}, - {"Expires", "Muddati tugaydi"}, - {"Non Expired Leases", "Muddati O'tmagan Leases"}, - {"Gateway", "Kirish yo'li"}, - {"TunnelID", "TunnelID"}, - {"EndDate", "Tugash Sanasi"}, - {"not floodfill", "floodfill emas"}, - {"Queue size", "Navbat hajmi"}, - {"Run peer test", "Sinovni boshlang"}, - {"Decline transit tunnels", "Tranzit tunnellarini rad etish"}, - {"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"}, - {"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qilish"}, - {"Start graceful shutdown", "Yumshoq to'xtashni boshlash"}, - {"Force shutdown", "Majburiy to'xtatish"}, - {"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"}, - {"Note: any action done here are not persistent and not changes your config files.", "Eslatma: shu yerda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."}, - {"Logging level", "Jurnal darajasi"}, - {"Transit tunnels limit", "Tranzit tunellarning chegarasi"}, - {"Change", "O'zgartirish"}, - {"Change language", "Tilni o'zgartirish"}, - {"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"}, - {"SAM disabled", "SAM o'chirilgan"}, - {"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"}, - {"SAM session not found", "SAM sessiyasi topilmadi"}, - {"SAM Session", "SAM sessiyasi"}, - {"Server Tunnels", "Server Tunellari"}, - {"Client Forwards", "Mijozlarni Yo'naltirish"}, - {"Server Forwards", "Serverni Yo'naltirish"}, - {"Unknown page", "Noma'lum sahifa"}, - {"Invalid token", "Noto‘g‘ri belgi"}, - {"SUCCESS", "Muvaffaqiyat"}, - {"Stream closed", "Strim yopiq"}, - {"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"}, - {"Destination not found", "Yo'nalish topilmadi"}, - {"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"}, - {"Return to destination page", "Manzilgoh sahifasiga qaytish"}, - {"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"}, - {"Back to commands list", "Buyruqlar ro'yxatiga qaytish"}, - {"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"}, - {"Description", "Tavsif"}, - {"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"}, - {"Submit", "Yuborish"}, - {"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"}, - {"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"}, - {"Such destination is not found", "Bunday yo'nalish topilmadi"}, - {"Unknown command", "Noma'lum buyruq"}, - {"Command accepted", "Buyruq qabul qilindi"}, - {"Proxy error", "Proksi xatosi"}, - {"Proxy info", "Proksi ma'lumotlari"}, - {"Proxy error: Host not found", "Proksi xatosi: Xost 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"}, - {"Invalid request", "Noto‘g‘ri so‘rov"}, - {"Proxy unable to parse your request", "Proksi sizning so'rovingizni aniqlab ololmayapti"}, - {"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"}, - {"Host", "Xost"}, - {"added to router's addressbook from helper", "'helper'dan routerning 'addressbook'ga qo'shildi"}, - {"Click here to proceed:", "Davom etish uchun shu yerni bosing:"}, - {"Continue", "Davom etish"}, - {"Addresshelper found", "Addresshelper topildi"}, - {"already in router's addressbook", "allaqachon 'addressbook'da yozilgan"}, - {"Click here to update record:", "Yozuvni yangilash uchun shu yerni bosing:"}, - {"invalid request uri", "noto'g'ri URI so'rovi"}, - {"Can't detect destination host from request", "So‘rov orqali manzil xostini aniqlab bo'lmayapti"}, - {"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"}, - {"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"}, - {"unknown outproxy url", "noma'lum outproxy url"}, - {"cannot resolve upstream proxy", "yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"}, - {"hostname too long", "xost nomi juda uzun"}, - {"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"}, - {"CONNECT error", "CONNECT xatosi"}, - {"Failed to Connect", "Ulanib bo'lmayapti"}, - {"socks proxy error", "'socks proxy' xatosi"}, - {"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"}, - {"cannot connect", "ulanib bo'lmaydi"}, - {"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"}, - {"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."}, - {"", ""}, - }; + static std::map strings + { + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "yaratilmoqda"}, + {"failed", "muvaffaqiyatsiz"}, + {"expiring", "muddati tugaydi"}, + {"established", "aloqa o'rnatildi"}, + {"unknown", "noma'lum"}, + {"exploratory", "tadqiqiy"}, + {"i2pd webconsole", "i2pd veb-konsoli"}, + {"Main page", "Asosiy sahifa"}, + {"Router commands", "Router buyruqlari"}, + {"Local Destinations", "Mahalliy joylanishlar"}, + {"LeaseSets", "LeaseSets"}, + {"Tunnels", "Tunnellar"}, + {"Transit Tunnels", "Tranzit Tunellari"}, + {"Transports", "Transportlar"}, + {"I2P tunnels", "I2P tunnellari"}, + {"SAM sessions", "SAM sessiyalari"}, + {"ERROR", "XATO"}, + {"OK", "OK"}, + {"Testing", "Testlash"}, + {"Firewalled", "Xavfsizlik devori bilan himoyalangan"}, + {"Unknown", "Notanish"}, + {"Proxy", "Proksi"}, + {"Mesh", "Mesh To'r"}, + {"Error", "Xato"}, + {"Clock skew", "Aniq vaqt emas"}, + {"Offline", "Oflayn"}, + {"Symmetric NAT", "Simmetrik NAT"}, + {"Uptime", "Ish vaqti"}, + {"Network status", "Tarmoq holati"}, + {"Network status v6", "Tarmoq holati v6"}, + {"Stopping in", "Ichida to'xtatish"}, + {"Family", "Oila"}, + {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, + {"Received", "Qabul qilindi"}, + {"KiB/s", "KiB/s"}, + {"Sent", "Yuborilgan"}, + {"Transit", "Tranzit"}, + {"Data path", "Ma'lumotlar joylanishi"}, + {"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."}, + {"Router Ident", "Router identifikatori"}, + {"Router Family", "Router oilasi"}, + {"Router Caps", "Router Bayroqlari"}, + {"Version", "Versiya"}, + {"Our external address", "Bizning tashqi manzilimiz"}, + {"supported", "qo'llab-quvvatlanadi"}, + {"Routers", "Routerlar"}, + {"Floodfills", "Floodfills"}, + {"Client Tunnels", "Mijoz Tunellari"}, + {"Services", "Xizmatlar"}, + {"Enabled", "Yoqilgan"}, + {"Disabled", "O'chirilgan"}, + {"Encrypted B33 address", "Shifrlangan B33 manzil"}, + {"Address registration line", "Manzilni ro'yxatga olish liniyasi"}, + {"Domain", "Domen"}, + {"Generate", "Yaratish"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Eslatma: 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"}, + {"Type", "Turi"}, + {"EncType", "ShifrlashTuri"}, + {"Inbound tunnels", "Kirish tunnellari"}, + {"ms", "ms"}, + {"Outbound tunnels", "Chiquvchi tunnellar"}, + {"Tags", "Teglar"}, + {"Incoming", "Kiruvchi"}, + {"Outgoing", "Chiquvchi"}, + {"Destination", "Manzilgoh"}, + {"Amount", "Soni"}, + {"Incoming Tags", "Kiruvchi teglar"}, + {"Tags sessions", "Teglar sessiyalari"}, + {"Status", "Holat"}, + {"Local Destination", "Mahalliy joylanish"}, + {"Streams", "Strim"}, + {"Close stream", "Strimni o'chirish"}, + {"I2CP session not found", "I2CP sessiyasi topilmadi"}, + {"I2CP is not enabled", "I2CP yoqilmagan"}, + {"Invalid", "Noto'g'ri"}, + {"Store type", "Saqlash turi"}, + {"Expires", "Muddati tugaydi"}, + {"Non Expired Leases", "Muddati O'tmagan Leases"}, + {"Gateway", "Kirish yo'li"}, + {"TunnelID", "TunnelID"}, + {"EndDate", "Tugash Sanasi"}, + {"not floodfill", "floodfill emas"}, + {"Queue size", "Navbat hajmi"}, + {"Run peer test", "Sinovni boshlang"}, + {"Decline transit tunnels", "Tranzit tunnellarini rad etish"}, + {"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"}, + {"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qilish"}, + {"Start graceful shutdown", "Yumshoq to'xtashni boshlash"}, + {"Force shutdown", "Majburiy to'xtatish"}, + {"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"}, + {"Note: any action done here are not persistent and not changes your config files.", "Eslatma: shu yerda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."}, + {"Logging level", "Jurnal darajasi"}, + {"Transit tunnels limit", "Tranzit tunellarning chegarasi"}, + {"Change", "O'zgartirish"}, + {"Change language", "Tilni o'zgartirish"}, + {"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"}, + {"SAM disabled", "SAM o'chirilgan"}, + {"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"}, + {"SAM session not found", "SAM sessiyasi topilmadi"}, + {"SAM Session", "SAM sessiyasi"}, + {"Server Tunnels", "Server Tunellari"}, + {"Client Forwards", "Mijozlarni Yo'naltirish"}, + {"Server Forwards", "Serverni Yo'naltirish"}, + {"Unknown page", "Noma'lum sahifa"}, + {"Invalid token", "Noto‘g‘ri belgi"}, + {"SUCCESS", "Muvaffaqiyat"}, + {"Stream closed", "Strim yopiq"}, + {"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"}, + {"Destination not found", "Yo'nalish topilmadi"}, + {"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"}, + {"Return to destination page", "Manzilgoh sahifasiga qaytish"}, + {"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"}, + {"Back to commands list", "Buyruqlar ro'yxatiga qaytish"}, + {"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"}, + {"Description", "Tavsif"}, + {"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"}, + {"Submit", "Yuborish"}, + {"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"}, + {"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"}, + {"Such destination is not found", "Bunday yo'nalish topilmadi"}, + {"Unknown command", "Noma'lum buyruq"}, + {"Command accepted", "Buyruq qabul qilindi"}, + {"Proxy error", "Proksi xatosi"}, + {"Proxy info", "Proksi ma'lumotlari"}, + {"Proxy error: Host not found", "Proksi xatosi: Xost 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"}, + {"Invalid request", "Noto‘g‘ri so‘rov"}, + {"Proxy unable to parse your request", "Proksi sizning so'rovingizni aniqlab ololmayapti"}, + {"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"}, + {"Host", "Xost"}, + {"added to router's addressbook from helper", "'helper'dan routerning 'addressbook'ga qo'shildi"}, + {"Click here to proceed:", "Davom etish uchun shu yerni bosing:"}, + {"Continue", "Davom etish"}, + {"Addresshelper found", "Addresshelper topildi"}, + {"already in router's addressbook", "allaqachon 'addressbook'da yozilgan"}, + {"Click here to update record:", "Yozuvni yangilash uchun shu yerni bosing:"}, + {"invalid request uri", "noto'g'ri URI so'rovi"}, + {"Can't detect destination host from request", "So‘rov orqali manzil xostini aniqlab bo'lmayapti"}, + {"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"}, + {"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"}, + {"unknown outproxy url", "noma'lum outproxy url"}, + {"cannot resolve upstream proxy", "yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"}, + {"hostname too long", "xost nomi juda uzun"}, + {"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"}, + {"CONNECT error", "CONNECT xatosi"}, + {"Failed to Connect", "Ulanib bo'lmayapti"}, + {"socks proxy error", "'socks proxy' xatosi"}, + {"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"}, + {"cannot connect", "ulanib bo'lmaydi"}, + {"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"}, + {"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."}, + {"", ""}, + }; - static std::map> plurals - { - {"days", {"kun", "kun"}}, - {"hours", {"soat", "soat"}}, - {"minutes", {"daqiqa", "daqiqa"}}, - {"seconds", {"soniya", "soniya"}}, - {"", {"", ""}}, - }; + static std::map > plurals + { + {"days", {"kun", "kun"}}, + {"hours", {"soat", "soat"}}, + {"minutes", {"daqiqa", "daqiqa"}}, + {"seconds", {"soniya", "soniya"}}, + {"", {"", ""}}, + }; - std::shared_ptr GetLocale() - { - return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); - } + std::shared_ptr GetLocale() { + return std::make_shared(language, strings, plurals, + [](int n) -> int { return plural(n); }); + } -} // language -} // i18n + } // language + } // i18n } // i2p diff --git a/libi2pd/Base.cpp b/libi2pd/Base.cpp index 94446e86..30933028 100644 --- a/libi2pd/Base.cpp +++ b/libi2pd/Base.cpp @@ -11,318 +11,296 @@ #include "Base.h" -namespace i2p -{ -namespace data -{ - static const char T32[32] = - { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', - 'y', 'z', '2', '3', '4', '5', '6', '7', - }; +namespace i2p { + namespace data { + static const char T32[32] = + { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '2', '3', '4', '5', '6', '7', + }; - const char * GetBase32SubstitutionTable () - { - return T32; - } + const char *GetBase32SubstitutionTable() { + return T32; + } - static void iT64Build(void); + static void iT64Build(void); - /* - * - * BASE64 Substitution Table - * ------------------------- - * - * Direct Substitution Table - */ + /* + * + * BASE64 Substitution Table + * ------------------------- + * + * Direct Substitution Table + */ - static const char T64[64] = - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '~' - }; + static const char T64[64] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '~' + }; - const char * GetBase64SubstitutionTable () - { - return T64; - } + const char *GetBase64SubstitutionTable() { + return T64; + } - /* - * Reverse Substitution Table (built in run time) - */ + /* + * Reverse Substitution Table (built in run time) + */ - static char iT64[256]; - static int isFirstTime = 1; + static char iT64[256]; + static int isFirstTime = 1; - /* - * Padding - */ + /* + * Padding + */ - static char P64 = '='; + static char P64 = '='; - /* - * - * ByteStreamToBase64 - * ------------------ - * - * Converts binary encoded data to BASE64 format. - * - */ + /* + * + * ByteStreamToBase64 + * ------------------ + * + * Converts binary encoded data to BASE64 format. + * + */ - size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */ - const uint8_t * InBuffer, /* Input buffer, binary data */ - size_t InCount, /* Number of bytes in the input buffer */ - char * OutBuffer, /* output buffer */ - size_t len /* length of output buffer */ - ) - { - unsigned char * ps; - unsigned char * pd; - unsigned char acc_1; - unsigned char acc_2; - int i; - int n; - int m; - size_t outCount; + size_t ByteStreamToBase64( /* Number of bytes in the encoded buffer */ + const uint8_t *InBuffer, /* Input buffer, binary data */ + size_t InCount, /* Number of bytes in the input buffer */ + char *OutBuffer, /* output buffer */ + size_t len /* length of output buffer */ + ) { + unsigned char *ps; + unsigned char *pd; + unsigned char acc_1; + unsigned char acc_2; + int i; + int n; + int m; + size_t outCount; - ps = (unsigned char *)InBuffer; - n = InCount / 3; - m = InCount % 3; - if (!m) - outCount = 4 * n; - else - outCount = 4 * (n + 1); + ps = (unsigned char *) InBuffer; + n = InCount / 3; + m = InCount % 3; + if (!m) + outCount = 4 * n; + else + outCount = 4 * (n + 1); - if (outCount > len) return 0; + if (outCount > len) return 0; - pd = (unsigned char *)OutBuffer; - for ( i = 0; i < n; i++ ) - { - acc_1 = *ps++; - acc_2 = (acc_1 << 4) & 0x30; - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - acc_1 = *ps++; - acc_2 |= acc_1 >> 4; /* base64 digit #2 */ - *pd++ = T64[acc_2]; - acc_1 &= 0x0f; - acc_1 <<= 2; - acc_2 = *ps++; - acc_1 |= acc_2 >> 6; /* base64 digit #3 */ - *pd++ = T64[acc_1]; - acc_2 &= 0x3f; /* base64 digit #4 */ - *pd++ = T64[acc_2]; - } - if ( m == 1 ) - { - acc_1 = *ps++; - acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - *pd++ = T64[acc_2]; - *pd++ = P64; - *pd++ = P64; + pd = (unsigned char *) OutBuffer; + for (i = 0; i < n; i++) { + acc_1 = *ps++; + acc_2 = (acc_1 << 4) & 0x30; + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<= 2; + acc_2 = *ps++; + acc_1 |= acc_2 >> 6; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + acc_2 &= 0x3f; /* base64 digit #4 */ + *pd++ = T64[acc_2]; + } + if (m == 1) { + acc_1 = *ps++; + acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + *pd++ = T64[acc_2]; + *pd++ = P64; + *pd++ = P64; - } - else if ( m == 2 ) - { - acc_1 = *ps++; - acc_2 = (acc_1 << 4) & 0x3f; - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - acc_1 = *ps++; - acc_2 |= acc_1 >> 4; /* base64 digit #2 */ - *pd++ = T64[acc_2]; - acc_1 &= 0x0f; - acc_1 <<= 2; /* base64 digit #3 */ - *pd++ = T64[acc_1]; - *pd++ = P64; - } + } else if (m == 2) { + acc_1 = *ps++; + acc_2 = (acc_1 << 4) & 0x3f; + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<= 2; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + *pd++ = P64; + } - return outCount; - } + return outCount; + } - /* - * - * Base64ToByteStream - * ------------------ - * - * Converts BASE64 encoded data to binary format. If input buffer is - * not properly padded, buffer of negative length is returned - * - */ + /* + * + * Base64ToByteStream + * ------------------ + * + * Converts BASE64 encoded data to binary format. If input buffer is + * not properly padded, buffer of negative length is returned + * + */ - size_t Base64ToByteStream ( /* Number of output bytes */ - const char * InBuffer, /* BASE64 encoded buffer */ - size_t InCount, /* Number of input bytes */ - uint8_t * OutBuffer, /* output buffer length */ - size_t len /* length of output buffer */ - ) - { - unsigned char * ps; - unsigned char * pd; - unsigned char acc_1; - unsigned char acc_2; - int i; - int n; - int m; - size_t outCount; + size_t Base64ToByteStream( /* Number of output bytes */ + const char *InBuffer, /* BASE64 encoded buffer */ + size_t InCount, /* Number of input bytes */ + uint8_t *OutBuffer, /* output buffer length */ + size_t len /* length of output buffer */ + ) { + unsigned char *ps; + unsigned char *pd; + unsigned char acc_1; + unsigned char acc_2; + int i; + int n; + int m; + size_t outCount; - if (isFirstTime) - iT64Build(); + if (isFirstTime) + iT64Build(); - n = InCount / 4; - m = InCount % 4; + n = InCount / 4; + m = InCount % 4; - if (InCount && !m) - outCount = 3 * n; - else - return 0; + if (InCount && !m) + outCount = 3 * n; + else + return 0; - ps = (unsigned char *)(InBuffer + InCount - 1); - while ( *ps-- == P64 ) - outCount--; - ps = (unsigned char *)InBuffer; + ps = (unsigned char *) (InBuffer + InCount - 1); + while (*ps-- == P64) + outCount--; + ps = (unsigned char *) InBuffer; - if (outCount > len) - return 0; + if (outCount > len) + return 0; - pd = OutBuffer; - auto endOfOutBuffer = OutBuffer + outCount; - for ( i = 0; i < n; i++ ) - { - acc_1 = iT64[*ps++]; - acc_2 = iT64[*ps++]; - acc_1 <<= 2; - acc_1 |= acc_2 >> 4; - *pd++ = acc_1; - if (pd >= endOfOutBuffer) - break; + pd = OutBuffer; + auto endOfOutBuffer = OutBuffer + outCount; + for (i = 0; i < n; i++) { + acc_1 = iT64[*ps++]; + acc_2 = iT64[*ps++]; + acc_1 <<= 2; + acc_1 |= acc_2 >> 4; + *pd++ = acc_1; + if (pd >= endOfOutBuffer) + break; - acc_2 <<= 4; - acc_1 = iT64[*ps++]; - acc_2 |= acc_1 >> 2; - *pd++ = acc_2; - if (pd >= endOfOutBuffer) - break; + acc_2 <<= 4; + acc_1 = iT64[*ps++]; + acc_2 |= acc_1 >> 2; + *pd++ = acc_2; + if (pd >= endOfOutBuffer) + break; - acc_2 = iT64[*ps++]; - acc_2 |= acc_1 << 6; - *pd++ = acc_2; - } + acc_2 = iT64[*ps++]; + acc_2 |= acc_1 << 6; + *pd++ = acc_2; + } - return outCount; - } + return outCount; + } - size_t Base64EncodingBufferSize (const size_t input_size) - { - auto d = div (input_size, 3); - if (d.rem) - d.quot++; + size_t Base64EncodingBufferSize(const size_t input_size) { + auto d = div(input_size, 3); + if (d.rem) + d.quot++; - return 4 * d.quot; - } + return 4 * d.quot; + } - std::string ToBase64Standard (const std::string& in) - { - auto len = Base64EncodingBufferSize (in.length ()); - char * str = new char[len + 1]; - auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len); - str[l] = 0; - // replace '-' by '+' and '~' by '/' - for (size_t i = 0; i < l; i++) - if (str[i] == '-') - str[i] = '+'; - else if (str[i] == '~') - str[i] = '/'; + std::string ToBase64Standard(const std::string &in) { + auto len = Base64EncodingBufferSize(in.length()); + char *str = new char[len + 1]; + auto l = ByteStreamToBase64((const uint8_t *) in.c_str(), in.length(), str, len); + str[l] = 0; + // replace '-' by '+' and '~' by '/' + for (size_t i = 0; i < l; i++) + if (str[i] == '-') + str[i] = '+'; + else if (str[i] == '~') + str[i] = '/'; - std::string s(str); - delete[] str; - return s; - } + std::string s(str); + delete[] str; + return s; + } - /* - * - * iT64 - * ---- - * Reverse table builder. P64 character is replaced with 0 - * - * - */ + /* + * + * iT64 + * ---- + * Reverse table builder. P64 character is replaced with 0 + * + * + */ - static void iT64Build() - { - int i; - isFirstTime = 0; - for ( i = 0; i < 256; i++ ) iT64[i] = -1; - for ( i = 0; i < 64; i++ ) iT64[(int)T64[i]] = i; - iT64[(int)P64] = 0; - } + static void iT64Build() { + int i; + isFirstTime = 0; + for (i = 0; i < 256; i++) iT64[i] = -1; + for (i = 0; i < 64; i++) iT64[(int) T64[i]] = i; + iT64[(int) P64] = 0; + } - size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) - { - int tmp = 0, bits = 0; - size_t ret = 0; - for (size_t i = 0; i < len; i++) - { - char ch = inBuf[i]; - if (ch >= '2' && ch <= '7') // digit - ch = (ch - '2') + 26; // 26 means a-z - else if (ch >= 'a' && ch <= 'z') - ch = ch - 'a'; // a = 0 - else - return 0; // unexpected character + size_t Base32ToByteStream(const char *inBuf, size_t len, uint8_t *outBuf, size_t outLen) { + int tmp = 0, bits = 0; + size_t ret = 0; + for (size_t i = 0; i < len; i++) { + char ch = inBuf[i]; + if (ch >= '2' && ch <= '7') // digit + ch = (ch - '2') + 26; // 26 means a-z + else if (ch >= 'a' && ch <= 'z') + ch = ch - 'a'; // a = 0 + else + return 0; // unexpected character - tmp |= ch; - bits += 5; - if (bits >= 8) - { - if (ret >= outLen) return ret; - outBuf[ret] = tmp >> (bits - 8); - bits -= 8; - ret++; - } - tmp <<= 5; - } - return ret; - } + tmp |= ch; + bits += 5; + if (bits >= 8) { + if (ret >= outLen) return ret; + outBuf[ret] = tmp >> (bits - 8); + bits -= 8; + ret++; + } + tmp <<= 5; + } + return ret; + } - size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) - { - size_t ret = 0, pos = 1; - int bits = 8, tmp = inBuf[0]; - while (ret < outLen && (bits > 0 || pos < len)) - { - if (bits < 5) - { - if (pos < len) - { - tmp <<= 8; - tmp |= inBuf[pos] & 0xFF; - pos++; - bits += 8; - } - else // last byte - { - tmp <<= (5 - bits); - bits = 5; - } - } + size_t ByteStreamToBase32(const uint8_t *inBuf, size_t len, char *outBuf, size_t outLen) { + size_t ret = 0, pos = 1; + int bits = 8, tmp = inBuf[0]; + while (ret < outLen && (bits > 0 || pos < len)) { + if (bits < 5) { + if (pos < len) { + tmp <<= 8; + tmp |= inBuf[pos] & 0xFF; + pos++; + bits += 8; + } else // last byte + { + tmp <<= (5 - bits); + bits = 5; + } + } - bits -= 5; - int ind = (tmp >> bits) & 0x1F; - outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); - ret++; - } - return ret; - } -} + bits -= 5; + int ind = (tmp >> bits) & 0x1F; + outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); + ret++; + } + return ret; + } + } } diff --git a/libi2pd/Base.h b/libi2pd/Base.h index 79152e02..946709c7 100644 --- a/libi2pd/Base.h +++ b/libi2pd/Base.h @@ -14,23 +14,27 @@ #include namespace i2p { -namespace data { - 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 (); + namespace data { + size_t ByteStreamToBase64(const uint8_t *InBuffer, size_t InCount, char *OutBuffer, size_t len); - 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); + size_t Base64ToByteStream(const char *InBuffer, size_t InCount, uint8_t *OutBuffer, size_t len); - /** - * 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); + const char *GetBase32SubstitutionTable(); - 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 #endif diff --git a/libi2pd/Blinding.cpp b/libi2pd/Blinding.cpp index 65e5f78c..ff9ce045 100644 --- a/libi2pd/Blinding.cpp +++ b/libi2pd/Blinding.cpp @@ -20,316 +20,310 @@ #include "Signature.h" #include "Blinding.h" -namespace i2p -{ -namespace data -{ - static EC_POINT * BlindPublicKeyECDSA (const EC_GROUP * group, const EC_POINT * pub, const uint8_t * seed) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order (group, q, ctx); - // calculate alpha = seed mod q - BIGNUM * alpha = BN_CTX_get (ctx); - BN_bin2bn (seed, 64, alpha); // seed is in BigEndian - BN_mod (alpha, alpha, q, ctx); // % q - // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) - auto p = EC_POINT_new (group); - EC_POINT_mul (group, p, alpha, nullptr, nullptr, ctx); // B*alpha - EC_POINT_add (group, p, pub, p, ctx); // pub + B*alpha - BN_CTX_end (ctx); - BN_CTX_free (ctx); - return p; - } +namespace i2p { + namespace data { + static EC_POINT *BlindPublicKeyECDSA(const EC_GROUP *group, const EC_POINT *pub, const uint8_t *seed) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(group, q, ctx); + // calculate alpha = seed mod q + BIGNUM *alpha = BN_CTX_get(ctx); + BN_bin2bn(seed, 64, alpha); // seed is in BigEndian + BN_mod(alpha, alpha, q, ctx); // % q + // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) + auto p = EC_POINT_new(group); + EC_POINT_mul(group, p, alpha, nullptr, nullptr, ctx); // B*alpha + EC_POINT_add(group, p, pub, p, ctx); // pub + B*alpha + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return p; + } - static void BlindPrivateKeyECDSA (const EC_GROUP * group, const BIGNUM * priv, const uint8_t * seed, BIGNUM * blindedPriv) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order (group, q, ctx); - // calculate alpha = seed mod q - BIGNUM * alpha = BN_CTX_get (ctx); - BN_bin2bn (seed, 64, alpha); // seed is in BigEndian - BN_mod (alpha, alpha, q, ctx); // % q - BN_add (alpha, alpha, priv); // alpha = alpha + priv - // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q - BN_mod (blindedPriv, alpha, q, ctx); // % q - BN_CTX_end (ctx); - BN_CTX_free (ctx); - } + static void + BlindPrivateKeyECDSA(const EC_GROUP *group, const BIGNUM *priv, const uint8_t *seed, BIGNUM *blindedPriv) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(group, q, ctx); + // calculate alpha = seed mod q + BIGNUM *alpha = BN_CTX_get(ctx); + BN_bin2bn(seed, 64, alpha); // seed is in BigEndian + BN_mod(alpha, alpha, q, ctx); // % q + BN_add(alpha, alpha, priv); // alpha = alpha + priv + // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q + BN_mod(blindedPriv, alpha, q, ctx); // % q + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } - static void BlindEncodedPublicKeyECDSA (size_t publicKeyLen, const EC_GROUP * group, const uint8_t * pub, const uint8_t * seed, uint8_t * blindedPub) - { - BIGNUM * x = BN_bin2bn (pub, publicKeyLen/2, NULL); - BIGNUM * y = BN_bin2bn (pub + publicKeyLen/2, publicKeyLen/2, NULL); - EC_POINT * p = EC_POINT_new (group); - EC_POINT_set_affine_coordinates_GFp (group, p, x, y, NULL); - EC_POINT * p1 = BlindPublicKeyECDSA (group, p, seed); - EC_POINT_free (p); - EC_POINT_get_affine_coordinates_GFp (group, p1, x, y, NULL); - EC_POINT_free (p1); - i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); - i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); - BN_free (x); BN_free (y); - } + static void + BlindEncodedPublicKeyECDSA(size_t publicKeyLen, const EC_GROUP *group, const uint8_t *pub, const uint8_t *seed, + uint8_t *blindedPub) { + BIGNUM *x = BN_bin2bn(pub, publicKeyLen / 2, NULL); + BIGNUM *y = BN_bin2bn(pub + publicKeyLen / 2, publicKeyLen / 2, NULL); + EC_POINT *p = EC_POINT_new(group); + EC_POINT_set_affine_coordinates_GFp(group, p, x, y, NULL); + EC_POINT *p1 = BlindPublicKeyECDSA(group, p, seed); + EC_POINT_free(p); + EC_POINT_get_affine_coordinates_GFp(group, p1, x, y, NULL); + EC_POINT_free(p1); + i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2); + i2p::crypto::bn2buf(y, blindedPub + publicKeyLen / 2, publicKeyLen / 2); + 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) - { - BIGNUM * a = BN_bin2bn (priv, publicKeyLen/2, NULL); - BIGNUM * a1 = BN_new (); - BlindPrivateKeyECDSA (group, a, seed, a1); - BN_free (a); - i2p::crypto::bn2buf (a1, blindedPriv, publicKeyLen/2); - auto p = EC_POINT_new (group); - BN_CTX * ctx = BN_CTX_new (); - EC_POINT_mul (group, p, a1, nullptr, nullptr, ctx); // B*a1 - BN_CTX_free (ctx); - BN_free (a1); - BIGNUM * x = BN_new(), * y = BN_new(); - EC_POINT_get_affine_coordinates_GFp (group, p, x, y, NULL); - EC_POINT_free (p); - i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); - 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) { + BIGNUM *a = BN_bin2bn(priv, publicKeyLen / 2, NULL); + BIGNUM *a1 = BN_new(); + BlindPrivateKeyECDSA(group, a, seed, a1); + BN_free(a); + i2p::crypto::bn2buf(a1, blindedPriv, publicKeyLen / 2); + auto p = EC_POINT_new(group); + BN_CTX *ctx = BN_CTX_new(); + EC_POINT_mul(group, p, a1, nullptr, nullptr, ctx); // B*a1 + BN_CTX_free(ctx); + BN_free(a1); + BIGNUM *x = BN_new(), *y = BN_new(); + EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL); + EC_POINT_free(p); + i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2); + i2p::crypto::bn2buf(y, blindedPub + publicKeyLen / 2, publicKeyLen / 2); + BN_free(x); + BN_free(y); + } - template - static size_t BlindECDSA (i2p::data::SigningKeyType sigType, const uint8_t * key, const uint8_t * seed, Fn blind, Args&&...args) - // blind is BlindEncodedPublicKeyECDSA or BlindEncodedPrivateKeyECDSA - { - size_t publicKeyLength = 0; - EC_GROUP * group = nullptr; - switch (sigType) - { - case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - { - publicKeyLength = i2p::crypto::ECDSAP256_KEY_LENGTH; - group = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); - break; - } - case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - { - publicKeyLength = i2p::crypto::ECDSAP384_KEY_LENGTH; - 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; - group = EC_GROUP_new_by_curve_name (NID_secp521r1); - break; - } - default: - LogPrint (eLogError, "Blinding: Signature type ", (int)sigType, " is not ECDSA"); - } - if (group) - { - blind (publicKeyLength, group, key, seed, std::forward(args)...); - EC_GROUP_free (group); - } - return publicKeyLength; - } + template + static size_t + 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; + switch (sigType) { + case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: { + publicKeyLength = i2p::crypto::ECDSAP256_KEY_LENGTH; + group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + break; + } + case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: { + publicKeyLength = i2p::crypto::ECDSAP384_KEY_LENGTH; + 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; + group = EC_GROUP_new_by_curve_name(NID_secp521r1); + break; + } + default: + LogPrint(eLogError, "Blinding: Signature type ", (int) sigType, " is not ECDSA"); + } + if (group) { + blind(publicKeyLength, group, key, seed, std::forward(args)...); + EC_GROUP_free(group); + } + return publicKeyLength; + } //---------------------------------------------------------- - const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01; - const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now - const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04; + 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_CLIENT_AUTH_FLAG = 0x04; - BlindedPublicKey::BlindedPublicKey (std::shared_ptr identity, bool clientAuth): - m_IsClientAuth (clientAuth) - { - if (!identity) return; - auto len = identity->GetSigningPublicKeyLen (); - m_PublicKey.resize (len); - memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len); - m_SigType = identity->GetSigningKeyType (); - if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) - m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11 - else - m_BlindedSigType = m_SigType; - } + BlindedPublicKey::BlindedPublicKey(std::shared_ptr identity, bool clientAuth) : + m_IsClientAuth(clientAuth) { + if (!identity) return; + auto len = identity->GetSigningPublicKeyLen(); + m_PublicKey.resize(len); + memcpy(m_PublicKey.data(), identity->GetSigningPublicKeyBuffer(), len); + m_SigType = identity->GetSigningKeyType(); + if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) + m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11 + else + m_BlindedSigType = m_SigType; + } - BlindedPublicKey::BlindedPublicKey (const std::string& b33): - m_SigType (0) // 0 means invalid, we can't blind DSA, set it later - { - uint8_t addr[40]; // TODO: define length from b33 - size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); - if (l < 32) - { - LogPrint (eLogError, "Blinding: Malformed b33 ", b33); - return; - } - uint32_t checksum = crc32 (0, addr + 3, l - 3); - // checksum is Little Endian - addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); - uint8_t flags = addr[0]; - 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; - } - else // one byte sig - { - m_SigType = addr[offset]; offset++; - m_BlindedSigType = addr[offset]; offset++; - } - m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG; + BlindedPublicKey::BlindedPublicKey(const std::string &b33) : + m_SigType(0) // 0 means invalid, we can't blind DSA, set it later + { + uint8_t addr[40]; // TODO: define length from b33 + size_t l = i2p::data::Base32ToByteStream(b33.c_str(), b33.length(), addr, 40); + if (l < 32) { + LogPrint(eLogError, "Blinding: Malformed b33 ", b33); + return; + } + uint32_t checksum = crc32(0, addr + 3, l - 3); + // checksum is Little Endian + addr[0] ^= checksum; + addr[1] ^= (checksum >> 8); + addr[2] ^= (checksum >> 16); + uint8_t flags = addr[0]; + 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; + } else // one byte sig + { + m_SigType = addr[offset]; + offset++; + m_BlindedSigType = addr[offset]; + offset++; + } + m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG; - std::unique_ptr blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType)); - if (blindedVerifier) - { - auto len = blindedVerifier->GetPublicKeyLen (); - if (offset + len <= l) - { - m_PublicKey.resize (len); - memcpy (m_PublicKey.data (), addr + offset, len); - } - else - LogPrint (eLogError, "Blinding: Public key in b33 address is too short for signature type ", (int)m_SigType); - } - else - LogPrint (eLogError, "Blinding: Unknown signature type ", (int)m_SigType, " in b33"); - } + std::unique_ptr blindedVerifier(i2p::data::IdentityEx::CreateVerifier(m_SigType)); + if (blindedVerifier) { + auto len = blindedVerifier->GetPublicKeyLen(); + if (offset + len <= l) { + m_PublicKey.resize(len); + memcpy(m_PublicKey.data(), addr + offset, len); + } else + LogPrint(eLogError, "Blinding: Public key in b33 address is too short for signature type ", + (int) m_SigType); + } else + LogPrint(eLogError, "Blinding: Unknown signature type ", (int) m_SigType, " in b33"); + } - std::string BlindedPublicKey::ToB33 () const - { - if (m_PublicKey.size () > 32) return ""; // assume 25519 - uint8_t addr[35]; char str[60]; // TODO: define actual length - uint8_t flags = 0; - if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG; - addr[0] = flags; // flags - addr[1] = m_SigType; // sig type - addr[2] = m_BlindedSigType; // blinded sig type - memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ()); - uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ()); - // checksum is Little Endian - addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); - auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60); - return std::string (str, str + l); - } + std::string BlindedPublicKey::ToB33() const { + if (m_PublicKey.size() > 32) return ""; // assume 25519 + uint8_t addr[35]; + char str[60]; // TODO: define actual length + uint8_t flags = 0; + if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG; + addr[0] = flags; // flags + addr[1] = m_SigType; // sig type + addr[2] = m_BlindedSigType; // blinded sig type + memcpy(addr + 3, m_PublicKey.data(), m_PublicKey.size()); + uint32_t checksum = crc32(0, addr + 3, m_PublicKey.size()); + // checksum is Little Endian + addr[0] ^= checksum; + addr[1] ^= (checksum >> 8); + addr[2] ^= (checksum >> 16); + auto l = ByteStreamToBase32(addr, m_PublicKey.size() + 3, str, 60); + return std::string(str, str + l); + } - void BlindedPublicKey::GetCredential (uint8_t * credential) const - { - // A = destination's signing public key - // stA = signature type of A, 2 bytes big endian - uint16_t stA = htobe16 (GetSigType ()); - // stA1 = signature type of blinded A, 2 bytes big endian - uint16_t stA1 = htobe16 (GetBlindedSigType ()); - // credential = H("credential", A || stA || stA1) - H ("credential", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, credential); - } + void BlindedPublicKey::GetCredential(uint8_t *credential) const { + // A = destination's signing public key + // stA = signature type of A, 2 bytes big endian + uint16_t stA = htobe16(GetSigType()); + // stA1 = signature type of blinded A, 2 bytes big endian + uint16_t stA1 = htobe16(GetBlindedSigType()); + // credential = H("credential", A || stA || stA1) + H("credential", {{GetPublicKey(), GetPublicKeyLen()}, + {(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 - { - uint8_t credential[32]; - GetCredential (credential); - // subcredential = H("subcredential", credential || blindedPublicKey) - H ("subcredential", { {credential, 32}, {blinded, len} }, subcredential); - } + void BlindedPublicKey::GetSubcredential(const uint8_t *blinded, size_t len, uint8_t *subcredential) const { + uint8_t credential[32]; + GetCredential(credential); + // subcredential = H("subcredential", credential || blindedPublicKey) + H("subcredential", {{credential, 32}, + {blinded, len}}, subcredential); + } - void BlindedPublicKey::GenerateAlpha (const char * date, uint8_t * seed) const - { - uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ()); - uint8_t salt[32]; - //seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64) - H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt); - i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed); - } + void BlindedPublicKey::GenerateAlpha(const char *date, uint8_t *seed) const { + uint16_t stA = htobe16(GetSigType()), stA1 = htobe16(GetBlindedSigType()); + uint8_t salt[32]; + //seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64) + H("I2PGenerateAlpha", {{GetPublicKey(), GetPublicKeyLen()}, + {(const uint8_t *) &stA, 2}, + {(const uint8_t *) &stA1, 2}}, salt); + i2p::crypto::HKDF(salt, (const uint8_t *) date, 8, "i2pblinding1", seed); + } - size_t BlindedPublicKey::GetBlindedKey (const char * date, uint8_t * blindedKey) const - { - uint8_t seed[64]; - GenerateAlpha (date, seed); + size_t BlindedPublicKey::GetBlindedKey(const char *date, uint8_t *blindedKey) const { + uint8_t seed[64]; + GenerateAlpha(date, seed); - size_t publicKeyLength = 0; - switch (m_SigType) - { - 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_SHA512_P521: - publicKeyLength = BlindECDSA (m_SigType, GetPublicKey (), seed, BlindEncodedPublicKeyECDSA, blindedKey); - break; - case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - i2p::crypto::GetEd25519 ()->BlindPublicKey (GetPublicKey (), seed, blindedKey); - publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; - break; - default: - LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType); - } - return publicKeyLength; - } + size_t publicKeyLength = 0; + switch (m_SigType) { + 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_SHA512_P521: + publicKeyLength = BlindECDSA(m_SigType, GetPublicKey(), seed, BlindEncodedPublicKeyECDSA, + blindedKey); + break; + case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: + case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + i2p::crypto::GetEd25519()->BlindPublicKey(GetPublicKey(), seed, blindedKey); + publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; + break; + default: + LogPrint(eLogError, "Blinding: Can't blind signature type ", (int) m_SigType); + } + return publicKeyLength; + } - size_t BlindedPublicKey::BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const - { - uint8_t seed[64]; - GenerateAlpha (date, seed); - size_t publicKeyLength = 0; - switch (m_SigType) - { - 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_SHA512_P521: - publicKeyLength = BlindECDSA (m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, blindedPub); - break; - case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub); - publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; - break; - case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - { - uint8_t exp[64]; - i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp); - i2p::crypto::GetEd25519 ()->BlindPrivateKey (exp, seed, blindedPriv, blindedPub); - publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; - break; - } - default: - LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType); - } - return publicKeyLength; - } + size_t BlindedPublicKey::BlindPrivateKey(const uint8_t *priv, const char *date, uint8_t *blindedPriv, + uint8_t *blindedPub) const { + uint8_t seed[64]; + GenerateAlpha(date, seed); + size_t publicKeyLength = 0; + switch (m_SigType) { + 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_SHA512_P521: + publicKeyLength = BlindECDSA(m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, + blindedPub); + break; + case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: + i2p::crypto::GetEd25519()->BlindPrivateKey(priv, seed, blindedPriv, blindedPub); + publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; + break; + case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: { + uint8_t exp[64]; + i2p::crypto::Ed25519::ExpandPrivateKey(priv, exp); + i2p::crypto::GetEd25519()->BlindPrivateKey(exp, seed, blindedPriv, blindedPub); + publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; + break; + } + default: + LogPrint(eLogError, "Blinding: Can't blind signature type ", (int) m_SigType); + } + return publicKeyLength; + } - void BlindedPublicKey::H (const std::string& p, const std::vector >& bufs, uint8_t * hash) const - { - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, p.c_str (), p.length ()); - for (const auto& it: bufs) - SHA256_Update (&ctx, it.first, it.second); - SHA256_Final (hash, &ctx); - } + void BlindedPublicKey::H(const std::string &p, const std::vector > &bufs, + uint8_t *hash) const { + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, p.c_str(), p.length()); + for (const auto &it: bufs) + SHA256_Update(&ctx, it.first, it.second); + SHA256_Final(hash, &ctx); + } - i2p::data::IdentHash BlindedPublicKey::GetStoreHash (const char * date) const - { - i2p::data::IdentHash hash; - uint8_t blinded[128]; - size_t publicKeyLength = 0; - if (date) - publicKeyLength = GetBlindedKey (date, blinded); - else - { - char currentDate[9]; - i2p::util::GetCurrentDate (currentDate); - publicKeyLength = GetBlindedKey (currentDate, blinded); - } - if (publicKeyLength) - { - auto stA1 = htobe16 (m_BlindedSigType); - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, (const uint8_t *)&stA1, 2); - SHA256_Update (&ctx, blinded, publicKeyLength); - SHA256_Final ((uint8_t *)hash, &ctx); - } - else - LogPrint (eLogError, "Blinding: Blinded key type ", (int)m_BlindedSigType, " is not supported"); - return hash; - } + i2p::data::IdentHash BlindedPublicKey::GetStoreHash(const char *date) const { + i2p::data::IdentHash hash; + uint8_t blinded[128]; + size_t publicKeyLength = 0; + if (date) + publicKeyLength = GetBlindedKey(date, blinded); + else { + char currentDate[9]; + i2p::util::GetCurrentDate(currentDate); + publicKeyLength = GetBlindedKey(currentDate, blinded); + } + if (publicKeyLength) { + auto stA1 = htobe16(m_BlindedSigType); + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (const uint8_t *) &stA1, 2); + SHA256_Update(&ctx, blinded, publicKeyLength); + SHA256_Final((uint8_t *) hash, &ctx); + } else + LogPrint(eLogError, "Blinding: Blinded key type ", (int) m_BlindedSigType, " is not supported"); + return hash; + } -} + } } diff --git a/libi2pd/Blinding.h b/libi2pd/Blinding.h index c78db003..aaa70467 100644 --- a/libi2pd/Blinding.h +++ b/libi2pd/Blinding.h @@ -14,42 +14,49 @@ #include #include "Identity.h" -namespace i2p -{ -namespace data -{ - class BlindedPublicKey // for encrypted LS2 - { - public: +namespace i2p { + namespace data { + class BlindedPublicKey // for encrypted LS2 + { + public: - BlindedPublicKey (std::shared_ptr identity, bool clientAuth = false); - BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p - std::string ToB33 () const; + BlindedPublicKey(std::shared_ptr identity, bool clientAuth = false); - const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; - size_t GetPublicKeyLen () const { return m_PublicKey.size (); }; - SigningKeyType GetSigType () const { return m_SigType; }; - SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; }; - bool IsValid () const { return GetSigType (); }; // signature type 0 means invalid + BlindedPublicKey(const std::string &b33); // from b33 without .b32.i2p + std::string ToB33() const; - 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 + const uint8_t *GetPublicKey() const { return m_PublicKey.data(); }; - private: + size_t GetPublicKeyLen() const { return m_PublicKey.size(); }; - 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 >& bufs, uint8_t * hash) const; + SigningKeyType GetSigType() const { return m_SigType; }; - private: + SigningKeyType GetBlindedSigType() const { return m_BlindedSigType; }; - std::vector m_PublicKey; - i2p::data::SigningKeyType m_SigType, m_BlindedSigType; - bool m_IsClientAuth = false; - }; -} + bool IsValid() const { return GetSigType(); }; // signature type 0 means invalid + + void GetSubcredential(const uint8_t *blinded, size_t len, uint8_t *subcredential) const; // 32 bytes + size_t GetBlindedKey(const char *date, + uint8_t *blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length + size_t BlindPrivateKey(const uint8_t *priv, const char *date, uint8_t *blindedPriv, + uint8_t *blindedPub) const; // date is 8 chars "YYYYMMDD", return public key length + 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 > &bufs, uint8_t *hash) const; + + private: + + std::vector m_PublicKey; + i2p::data::SigningKeyType m_SigType, m_BlindedSigType; + bool m_IsClientAuth = false; + }; + } } #endif diff --git a/libi2pd/BloomFilter.cpp b/libi2pd/BloomFilter.cpp index de077e60..67d55678 100644 --- a/libi2pd/BloomFilter.cpp +++ b/libi2pd/BloomFilter.cpp @@ -11,67 +11,58 @@ #include #include -namespace i2p -{ -namespace util -{ +namespace i2p { + namespace util { - /** @brief decaying bloom filter implementation */ - class DecayingBloomFilter : public IBloomFilter - { - public: + /** @brief decaying bloom filter implementation */ + class DecayingBloomFilter : public IBloomFilter { + public: - DecayingBloomFilter(const std::size_t size) - { - m_Size = size; - m_Data = new uint8_t[size]; - } + DecayingBloomFilter(const std::size_t size) { + m_Size = size; + m_Data = new uint8_t[size]; + } - /** @brief implements IBloomFilter::~IBloomFilter */ - ~DecayingBloomFilter() - { - delete [] m_Data; - } + /** @brief implements IBloomFilter::~IBloomFilter */ + ~DecayingBloomFilter() { + delete[] m_Data; + } - /** @brief implements IBloomFilter::Add */ - bool Add(const uint8_t * data, std::size_t len) - { - std::size_t idx; - uint8_t mask; - Get(data, len, idx, mask); - if(m_Data[idx] & mask) return false; // filter hit - m_Data[idx] |= mask; - return true; - } + /** @brief implements IBloomFilter::Add */ + bool Add(const uint8_t *data, std::size_t len) { + std::size_t idx; + uint8_t mask; + Get(data, len, idx, mask); + if (m_Data[idx] & mask) return false; // filter hit + m_Data[idx] |= mask; + return true; + } - /** @brief implements IBloomFilter::Decay */ - void Decay() - { - // reset bloom filter buffer - memset(m_Data, 0, m_Size); - } + /** @brief implements IBloomFilter::Decay */ + void Decay() { + // reset bloom filter buffer + memset(m_Data, 0, m_Size); + } - private: - /** @brief get bit index for for data */ - void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm) - { - bm = 1; - uint8_t digest[32]; - // TODO: use blake2 because it's faster - SHA256(data, len, digest); - uint64_t i = buf64toh(digest); - idx = i % m_Size; - bm <<= (i % 8); - } + private: + /** @brief get bit index for for data */ + void Get(const uint8_t *data, std::size_t len, std::size_t &idx, uint8_t &bm) { + bm = 1; + uint8_t digest[32]; + // TODO: use blake2 because it's faster + SHA256(data, len, digest); + uint64_t i = buf64toh(digest); + idx = i % m_Size; + bm <<= (i % 8); + } - uint8_t * m_Data; - std::size_t m_Size; - }; + uint8_t *m_Data; + std::size_t m_Size; + }; - BloomFilterPtr BloomFilter(std::size_t capacity) - { - return std::make_shared(capacity); - } -} + BloomFilterPtr BloomFilter(std::size_t capacity) { + return std::make_shared(capacity); + } + } } diff --git a/libi2pd/BloomFilter.h b/libi2pd/BloomFilter.h index ade854e4..eb4d0a51 100644 --- a/libi2pd/BloomFilter.h +++ b/libi2pd/BloomFilter.h @@ -8,32 +8,32 @@ #ifndef BLOOM_FILTER_H_ #define BLOOM_FILTER_H_ + #include #include -namespace i2p -{ -namespace util -{ +namespace i2p { + namespace util { - /** @brief interface for bloom filter */ - struct IBloomFilter - { + /** @brief interface for bloom filter */ + struct IBloomFilter { - /** @brief destructor */ - 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; - }; + /** @brief destructor */ + virtual ~IBloomFilter() {}; - typedef std::shared_ptr 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 */ - BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8); + /** @brief optionally decay old entries */ + virtual void Decay() = 0; + }; -} + typedef std::shared_ptr BloomFilterPtr; + + /** @brief create bloom filter */ + BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8); + + } } #endif diff --git a/libi2pd/CPU.cpp b/libi2pd/CPU.cpp index 0804e2ac..130e8221 100644 --- a/libi2pd/CPU.cpp +++ b/libi2pd/CPU.cpp @@ -7,9 +7,11 @@ */ #include "CPU.h" + #if defined(__x86_64__) || defined(__i386__) #include #endif + #include "Log.h" #ifndef bit_AES @@ -20,39 +22,36 @@ #endif -namespace i2p -{ -namespace cpu -{ - bool aesni = false; - bool avx = false; +namespace i2p { + namespace cpu { + 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__) - int info[4]; - __cpuid(0, info[0], info[1], info[2], info[3]); - if (info[0] >= 0x00000001) { - __cpuid(0x00000001, info[0], info[1], info[2], info[3]); + int info[4]; + __cpuid(0, info[0], info[1], info[2], info[3]); + if (info[0] >= 0x00000001) { + __cpuid(0x00000001, info[0], info[1], info[2], info[3]); #if defined (_WIN32) && (WINVER == 0x0501) // WinXP - if (AesSwitch && force) { // only if forced + if (AesSwitch && force) { // only if forced #else - if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) { + if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) { #endif - aesni = true; - } + aesni = true; + } #if defined (_WIN32) && (WINVER == 0x0501) // WinXP - if (AvxSwitch && force) { // only if forced + if (AvxSwitch && force) { // only if forced #else - if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) { + if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) { #endif - avx = true; - } - } + avx = true; + } + } #endif // defined(__x86_64__) || defined(__i386__) - LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled")); - LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled")); - } -} + LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled")); + LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled")); + } + } } diff --git a/libi2pd/CPU.h b/libi2pd/CPU.h index f021bccb..3e1ee27c 100644 --- a/libi2pd/CPU.h +++ b/libi2pd/CPU.h @@ -9,15 +9,13 @@ #ifndef LIBI2PD_CPU_H #define LIBI2PD_CPU_H -namespace i2p -{ -namespace cpu -{ - extern bool aesni; - extern bool avx; +namespace i2p { + namespace cpu { + extern bool aesni; + extern bool avx; - void Detect(bool AesSwitch, bool AvxSwitch, bool force); -} + void Detect(bool AesSwitch, bool AvxSwitch, bool force); + } } #endif diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp index 66bc135f..411cb78d 100644 --- a/libi2pd/ChaCha20.cpp +++ b/libi2pd/ChaCha20.cpp @@ -13,125 +13,112 @@ #include "ChaCha20.h" #if !OPENSSL_AEAD_CHACHA20_POLY1305 -namespace i2p -{ -namespace crypto -{ -namespace chacha -{ -void u32t8le(uint32_t v, uint8_t * p) -{ - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; - p[2] = (v >> 16) & 0xff; - p[3] = (v >> 24) & 0xff; -} +namespace i2p { + namespace crypto { + namespace chacha { + void u32t8le(uint32_t v, uint8_t *p) { + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; + } -uint32_t u8t32le(const uint8_t * p) -{ - uint32_t value = p[3]; + uint32_t u8t32le(const uint8_t *p) { + uint32_t value = p[3]; - value = (value << 8) | p[2]; - value = (value << 8) | p[1]; - value = (value << 8) | p[0]; + value = (value << 8) | p[2]; + value = (value << 8) | p[1]; + value = (value << 8) | p[0]; - return value; -} + return value; + } -uint32_t rotl32(uint32_t x, int n) -{ - return x << n | (x >> (-n & 31)); -} + uint32_t rotl32(uint32_t x, int n) { + return x << n | (x >> (-n & 31)); + } -void quarterround(uint32_t *x, int a, int b, int c, int d) -{ - x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); - x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); - x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); - x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); -} + void quarterround(uint32_t *x, int a, int b, int c, int d) { + x[a] += x[b]; + x[d] = rotl32(x[d] ^ x[a], 16); + x[c] += x[d]; + x[b] = rotl32(x[b] ^ x[c], 12); + x[a] += x[b]; + x[d] = rotl32(x[d] ^ x[a], 8); + x[c] += x[d]; + x[b] = rotl32(x[b] ^ x[c], 7); + } -void Chacha20Block::operator << (const Chacha20State & st) -{ - int i; - for (i = 0; i < 16; i++) - u32t8le(st.data[i], data + (i << 2)); -} + void Chacha20Block::operator<<(const Chacha20State &st) { + int i; + for (i = 0; i < 16; i++) + u32t8le(st.data[i], data + (i << 2)); + } -void block (Chacha20State &input, int rounds) -{ - int i; - Chacha20State x; - x.Copy(input); + void block(Chacha20State &input, int rounds) { + int i; + Chacha20State x; + x.Copy(input); - for (i = rounds; i > 0; i -= 2) - { - quarterround(x.data, 0, 4, 8, 12); - quarterround(x.data, 1, 5, 9, 13); - quarterround(x.data, 2, 6, 10, 14); - quarterround(x.data, 3, 7, 11, 15); - quarterround(x.data, 0, 5, 10, 15); - quarterround(x.data, 1, 6, 11, 12); - quarterround(x.data, 2, 7, 8, 13); - quarterround(x.data, 3, 4, 9, 14); - } - x += input; - input.block << x; -} + for (i = rounds; i > 0; i -= 2) { + quarterround(x.data, 0, 4, 8, 12); + quarterround(x.data, 1, 5, 9, 13); + quarterround(x.data, 2, 6, 10, 14); + quarterround(x.data, 3, 7, 11, 15); + quarterround(x.data, 0, 5, 10, 15); + quarterround(x.data, 1, 6, 11, 12); + quarterround(x.data, 2, 7, 8, 13); + quarterround(x.data, 3, 4, 9, 14); + } + x += input; + input.block << x; + } -void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter) -{ - state.data[0] = 0x61707865; - state.data[1] = 0x3320646e; - state.data[2] = 0x79622d32; - state.data[3] = 0x6b206574; - for (size_t i = 0; i < 8; i++) - state.data[4 + i] = chacha::u8t32le(key + i * 4); + void Chacha20Init(Chacha20State &state, const uint8_t *nonce, const uint8_t *key, uint32_t counter) { + state.data[0] = 0x61707865; + state.data[1] = 0x3320646e; + state.data[2] = 0x79622d32; + state.data[3] = 0x6b206574; + for (size_t i = 0; i < 8; i++) + state.data[4 + i] = chacha::u8t32le(key + i * 4); - state.data[12] = htole32 (counter); - for (size_t i = 0; i < 3; i++) - state.data[13 + i] = chacha::u8t32le(nonce + i * 4); -} + state.data[12] = htole32 (counter); + for (size_t i = 0; i < 3; i++) + state.data[13 + i] = chacha::u8t32le(nonce + i * 4); + } -void Chacha20SetCounter (Chacha20State& state, uint32_t counter) -{ - state.data[12] = htole32 (counter); - state.offset = 0; -} + void Chacha20SetCounter(Chacha20State &state, uint32_t counter) { + state.data[12] = htole32 (counter); + state.offset = 0; + } -void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz) -{ - if (state.offset > 0) - { - // previous block if any - auto s = chacha::blocksize - state.offset; - if (sz < s) s = sz; - for (size_t i = 0; i < s; i++) - buf[i] ^= state.block.data[state.offset + i]; - buf += s; - sz -= s; - state.offset += s; - if (state.offset >= chacha::blocksize) state.offset = 0; - } - for (size_t i = 0; i < sz; i += chacha::blocksize) - { - chacha::block(state, chacha::rounds); - state.data[12]++; - for (size_t j = i; j < i + chacha::blocksize; j++) - { - if (j >= sz) - { - state.offset = j & 0x3F; // % 64 - break; - } - buf[j] ^= state.block.data[j - i]; - } - } -} + void Chacha20Encrypt(Chacha20State &state, uint8_t *buf, size_t sz) { + if (state.offset > 0) { + // previous block if any + auto s = chacha::blocksize - state.offset; + if (sz < s) s = sz; + for (size_t i = 0; i < s; i++) + buf[i] ^= state.block.data[state.offset + i]; + buf += s; + sz -= s; + state.offset += s; + if (state.offset >= chacha::blocksize) state.offset = 0; + } + for (size_t i = 0; i < sz; i += chacha::blocksize) { + chacha::block(state, chacha::rounds); + state.data[12]++; + for (size_t j = i; j < i + chacha::blocksize; j++) { + if (j >= sz) { + state.offset = j & 0x3F; // % 64 + break; + } + buf[j] ^= state.block.data[j - i]; + } + } + } -} // namespace chacha -} // namespace crypto + } // namespace chacha + } // namespace crypto } // namespace i2p #endif diff --git a/libi2pd/ChaCha20.h b/libi2pd/ChaCha20.h index 4364024b..6b3d1a10 100644 --- a/libi2pd/ChaCha20.h +++ b/libi2pd/ChaCha20.h @@ -10,6 +10,7 @@ */ #ifndef LIBI2PD_CHACHA20_H #define LIBI2PD_CHACHA20_H + #include #include #include @@ -17,55 +18,55 @@ #include "Crypto.h" #if !OPENSSL_AEAD_CHACHA20_POLY1305 -namespace i2p -{ -namespace crypto -{ - const std::size_t CHACHA20_KEY_BYTES = 32; - const std::size_t CHACHA20_NOUNCE_BYTES = 12; +namespace i2p { + namespace crypto { + const std::size_t CHACHA20_KEY_BYTES = 32; + const std::size_t CHACHA20_NOUNCE_BYTES = 12; -namespace chacha -{ - constexpr std::size_t blocksize = 64; - constexpr int rounds = 20; + namespace chacha { + constexpr std::size_t + blocksize = 64; + constexpr int rounds = 20; - struct Chacha20State; - struct Chacha20Block - { - Chacha20Block () {}; - Chacha20Block (Chacha20Block &&) = delete; + struct Chacha20State; - uint8_t data[blocksize]; + struct Chacha20Block { + Chacha20Block() {}; - void operator << (const Chacha20State & st); - }; + Chacha20Block(Chacha20Block &&) = delete; - struct Chacha20State - { - Chacha20State (): offset (0) {}; - Chacha20State (Chacha20State &&) = delete; + uint8_t data[blocksize]; - Chacha20State & operator += (const Chacha20State & other) - { - for(int i = 0; i < 16; i++) - data[i] += other.data[i]; - return *this; - } + void operator<<(const Chacha20State &st); + }; - void Copy(const Chacha20State & other) - { - memcpy(data, other.data, sizeof(uint32_t) * 16); - } - uint32_t data[16]; - Chacha20Block block; - size_t offset; - }; + struct Chacha20State { + Chacha20State() : offset(0) {}; - 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 + Chacha20State(Chacha20State &&) = delete; + + Chacha20State &operator+=(const Chacha20State &other) { + for (int i = 0; i < 16; i++) + 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 #endif diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 56da9b7c..e95d8824 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -24,425 +24,471 @@ using namespace boost::program_options; namespace i2p { -namespace config { - options_description m_OptionsDesc; - variables_map m_Options; + namespace config { + options_description m_OptionsDesc; + variables_map m_Options; - void Init() - { - options_description general("General options"); - general.add_options() - ("help", "Show this message") - ("version", "Show i2pd version") - ("conf", value()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") - ("tunconf", value()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") - ("tunnelsdir", value()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") - ("certsdir", value()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates") - ("pidfile", value()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") - ("log", value()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") - ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") - ("loglevel", value()->default_value("warn"), "Set the minimal level of log messages (debug, info, warn, error, none)") - ("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)") - ("family", value()->default_value(""), "Specify a family, router belongs to") - ("datadir", value()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)") - ("host", value()->default_value("0.0.0.0"), "External IP") - ("ifname", value()->default_value(""), "Network interface to bind to") - ("ifname4", value()->default_value(""), "Network interface to bind to for ipv4") - ("ifname6", value()->default_value(""), "Network interface to bind to for ipv6") - ("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)") - ("port", value()->default_value(0), "Port to listen for incoming connections (default: auto)") - ("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)") - ("address4", value()->default_value(""), "Local address to bind ipv4 transport sockets to") - ("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") - ("address6", value()->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()->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()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") - ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") - ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") - ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") - ("ntcpproxy", value()->default_value(""), "Ignored") + void Init() { + options_description general("General options"); + general.add_options() + ("help", "Show this message") + ("version", "Show i2pd version") + ("conf", value()->default_value(""), + "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") + ("tunconf", value()->default_value(""), + "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") + ("tunnelsdir", value()->default_value(""), + "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") + ("certsdir", value()->default_value(""), + "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates") + ("pidfile", value()->default_value(""), + "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") + ("log", value()->default_value(""), + "Logs destination: stdout, file, syslog (stdout if not set)") + ("logfile", value()->default_value(""), + "Path to logfile (stdout if not set, autodetect if daemon)") + ("loglevel", value()->default_value("warn"), + "Set the minimal level of log messages (debug, info, warn, error, none)") + ("logclftime", bool_switch()->default_value(false), + "Write full CLF-formatted date and time to log (default: disabled, write only time)") + ("family", value()->default_value(""), "Specify a family, router belongs to") + ("datadir", value()->default_value(""), + "Path to storage of i2pd data (RI, keys, peer profiles, ...)") + ("host", value()->default_value("0.0.0.0"), "External IP") + ("ifname", value()->default_value(""), "Network interface to bind to") + ("ifname4", value()->default_value(""), "Network interface to bind to for ipv4") + ("ifname6", value()->default_value(""), "Network interface to bind to for ipv6") + ("nat", bool_switch()->default_value(true), + "Should we assume we are behind NAT? (default: enabled)") + ("port", value()->default_value(0), + "Port to listen for incoming connections (default: auto)") + ("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)") + ("address4", value()->default_value(""), + "Local address to bind ipv4 transport sockets to") + ("ipv6", bool_switch()->default_value(false), + "Enable communication through ipv6 (default: disabled)") + ("address6", value()->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()->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()->default_value(""), + "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") + ("share", value()->default_value(100), + "Limit of transit traffic from max bandwidth in percents. (default: 100)") + ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") + ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") + ("ntcpproxy", value()->default_value(""), "Ignored") #ifdef _WIN32 - ("svcctl", value()->default_value(""), "Ignored") - ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") - ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") + ("svcctl", value()->default_value(""), "Ignored") + ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") + ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") #endif - ; + ; - options_description limits("Limits options"); - limits.add_options() - ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") - ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") - ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") - ("limits.ntcpsoft", value()->default_value(0), "Ignored") - ("limits.ntcphard", value()->default_value(0), "Ignored") - ("limits.ntcpthreads", value()->default_value(1), "Ignored") - ; + options_description limits("Limits options"); + limits.add_options() + ("limits.coresize", value()->default_value(0), + "Maximum size of corefile in Kb (0 - use system limit)") + ("limits.openfiles", value()->default_value(0), + "Maximum number of open files (0 - use system default)") + ("limits.transittunnels", value()->default_value(2500), + "Maximum active transit sessions (default:2500)") + ("limits.ntcpsoft", value()->default_value(0), "Ignored") + ("limits.ntcphard", value()->default_value(0), "Ignored") + ("limits.ntcpthreads", value()->default_value(1), "Ignored"); - options_description httpserver("HTTP Server options"); - httpserver.add_options() - ("http.enabled", value()->default_value(true), "Enable or disable webconsole") - ("http.address", value()->default_value("127.0.0.1"), "Webconsole listen address") - ("http.port", value()->default_value(7070), "Webconsole listen port") - ("http.auth", value()->default_value(false), "Enable Basic HTTP auth for webconsole") - ("http.user", value()->default_value("i2pd"), "Username for basic auth") - ("http.pass", value()->default_value(""), "Password for basic auth (default: random, see logs)") - ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") - ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") - ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") - ("http.lang", value()->default_value("english"), "WebUI language (default: english )") - ; + options_description httpserver("HTTP Server options"); + httpserver.add_options() + ("http.enabled", value()->default_value(true), "Enable or disable webconsole") + ("http.address", value()->default_value("127.0.0.1"), "Webconsole listen address") + ("http.port", value()->default_value(7070), "Webconsole listen port") + ("http.auth", value()->default_value(false), "Enable Basic HTTP auth for webconsole") + ("http.user", value()->default_value("i2pd"), "Username for basic auth") + ("http.pass", value()->default_value(""), + "Password for basic auth (default: random, see logs)") + ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") + ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") + ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") + ("http.lang", value()->default_value("english"), "WebUI language (default: english )"); - options_description httpproxy("HTTP Proxy options"); - httpproxy.add_options() - ("httpproxy.enabled", value()->default_value(true), "Enable or disable HTTP Proxy") - ("httpproxy.address", value()->default_value("127.0.0.1"), "HTTP Proxy listen address") - ("httpproxy.port", value()->default_value(4444), "HTTP Proxy listen port") - ("httpproxy.keys", value()->default_value("transient-proxy"), "File to persist HTTP Proxy keys. Transient by default") - ("httpproxy.signaturetype", value()-> - default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") - ("httpproxy.inbound.length", value()->default_value("3"), "HTTP proxy inbound tunnel length") - ("httpproxy.outbound.length", value()->default_value("3"), "HTTP proxy outbound tunnel length") - ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") - ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") - ("httpproxy.inbound.lengthVariance", value()->default_value("0"), "HTTP proxy inbound tunnels length variance") - ("httpproxy.outbound.lengthVariance", value()->default_value("0"), "HTTP proxy outbound tunnels length variance") - ("httpproxy.latency.min", value()->default_value("0"), "HTTP proxy min latency for tunnels") - ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") - ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") - ("httpproxy.addresshelper", value()->default_value(true), "Enable or disable addresshelper") - ("httpproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") - ("httpproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") - ("httpproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") - ; + options_description httpproxy("HTTP Proxy options"); + httpproxy.add_options() + ("httpproxy.enabled", value()->default_value(true), "Enable or disable HTTP Proxy") + ("httpproxy.address", value()->default_value("127.0.0.1"), "HTTP Proxy listen address") + ("httpproxy.port", value()->default_value(4444), "HTTP Proxy listen port") + ("httpproxy.keys", value()->default_value("transient-proxy"), + "File to persist HTTP Proxy keys. Transient by default") + ("httpproxy.signaturetype", value()-> + default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), + "Signature type for new keys. 7 (EdDSA) by default") + ("httpproxy.inbound.length", value()->default_value("3"), + "HTTP proxy inbound tunnel length") + ("httpproxy.outbound.length", value()->default_value("3"), + "HTTP proxy outbound tunnel length") + ("httpproxy.inbound.quantity", value()->default_value("5"), + "HTTP proxy inbound tunnels quantity") + ("httpproxy.outbound.quantity", value()->default_value("5"), + "HTTP proxy outbound tunnels quantity") + ("httpproxy.inbound.lengthVariance", value()->default_value("0"), + "HTTP proxy inbound tunnels length variance") + ("httpproxy.outbound.lengthVariance", value()->default_value("0"), + "HTTP proxy outbound tunnels length variance") + ("httpproxy.latency.min", value()->default_value("0"), + "HTTP proxy min latency for tunnels") + ("httpproxy.latency.max", value()->default_value("0"), + "HTTP proxy max latency for tunnels") + ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") + ("httpproxy.addresshelper", value()->default_value(true), "Enable or disable addresshelper") + ("httpproxy.i2cp.leaseSetType", value()->default_value("3"), + "Local destination's LeaseSet type") + ("httpproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), + "Local destination's LeaseSet encryption type") + ("httpproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key"); - options_description socksproxy("SOCKS Proxy options"); - socksproxy.add_options() - ("socksproxy.enabled", value()->default_value(true), "Enable or disable SOCKS Proxy") - ("socksproxy.address", value()->default_value("127.0.0.1"), "SOCKS Proxy listen address") - ("socksproxy.port", value()->default_value(4447), "SOCKS Proxy listen port") - ("socksproxy.keys", value()->default_value("transient-proxy"), "File to persist SOCKS Proxy keys. Transient by default") - ("socksproxy.signaturetype", value()-> - default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") - ("socksproxy.inbound.length", value()->default_value("3"), "SOCKS proxy inbound tunnel length") - ("socksproxy.outbound.length", value()->default_value("3"), "SOCKS proxy outbound tunnel length") - ("socksproxy.inbound.quantity", value()->default_value("5"), "SOCKS proxy inbound tunnels quantity") - ("socksproxy.outbound.quantity", value()->default_value("5"), "SOCKS proxy outbound tunnels quantity") - ("socksproxy.inbound.lengthVariance", value()->default_value("0"), "SOCKS proxy inbound tunnels length variance") - ("socksproxy.outbound.lengthVariance", value()->default_value("0"), "SOCKS proxy outbound tunnels length variance") - ("socksproxy.latency.min", value()->default_value("0"), "SOCKS proxy min latency for tunnels") - ("socksproxy.latency.max", value()->default_value("0"), "SOCKS proxy max latency for tunnels") - ("socksproxy.outproxy.enabled", value()->default_value(false), "Enable or disable SOCKS outproxy") - ("socksproxy.outproxy", value()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") - ("socksproxy.outproxyport", value()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") - ("socksproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") - ("socksproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") - ("socksproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") - ; + options_description socksproxy("SOCKS Proxy options"); + socksproxy.add_options() + ("socksproxy.enabled", value()->default_value(true), "Enable or disable SOCKS Proxy") + ("socksproxy.address", value()->default_value("127.0.0.1"), + "SOCKS Proxy listen address") + ("socksproxy.port", value()->default_value(4447), "SOCKS Proxy listen port") + ("socksproxy.keys", value()->default_value("transient-proxy"), + "File to persist SOCKS Proxy keys. Transient by default") + ("socksproxy.signaturetype", value()-> + default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), + "Signature type for new keys. 7 (EdDSA) by default") + ("socksproxy.inbound.length", value()->default_value("3"), + "SOCKS proxy inbound tunnel length") + ("socksproxy.outbound.length", value()->default_value("3"), + "SOCKS proxy outbound tunnel length") + ("socksproxy.inbound.quantity", value()->default_value("5"), + "SOCKS proxy inbound tunnels quantity") + ("socksproxy.outbound.quantity", value()->default_value("5"), + "SOCKS proxy outbound tunnels quantity") + ("socksproxy.inbound.lengthVariance", value()->default_value("0"), + "SOCKS proxy inbound tunnels length variance") + ("socksproxy.outbound.lengthVariance", value()->default_value("0"), + "SOCKS proxy outbound tunnels length variance") + ("socksproxy.latency.min", value()->default_value("0"), + "SOCKS proxy min latency for tunnels") + ("socksproxy.latency.max", value()->default_value("0"), + "SOCKS proxy max latency for tunnels") + ("socksproxy.outproxy.enabled", value()->default_value(false), + "Enable or disable SOCKS outproxy") + ("socksproxy.outproxy", value()->default_value("127.0.0.1"), + "Upstream outproxy address for SOCKS Proxy") + ("socksproxy.outproxyport", value()->default_value(9050), + "Upstream outproxy port for SOCKS Proxy") + ("socksproxy.i2cp.leaseSetType", value()->default_value("3"), + "Local destination's LeaseSet type") + ("socksproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), + "Local destination's LeaseSet encryption type") + ("socksproxy.i2cp.leaseSetPrivKey", value()->default_value(""), + "LeaseSet private key"); - options_description sam("SAM bridge options"); - sam.add_options() - ("sam.enabled", value()->default_value(true), "Enable or disable SAM Application bridge") - ("sam.address", value()->default_value("127.0.0.1"), "SAM listen address") - ("sam.port", value()->default_value(7656), "SAM listen port") - ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread") - ; + options_description sam("SAM bridge options"); + sam.add_options() + ("sam.enabled", value()->default_value(true), "Enable or disable SAM Application bridge") + ("sam.address", value()->default_value("127.0.0.1"), "SAM listen address") + ("sam.port", value()->default_value(7656), "SAM listen port") + ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread"); - options_description bob("BOB options"); - bob.add_options() - ("bob.enabled", value()->default_value(false), "Enable or disable BOB command channel") - ("bob.address", value()->default_value("127.0.0.1"), "BOB listen address") - ("bob.port", value()->default_value(2827), "BOB listen port") - ; + options_description bob("BOB options"); + bob.add_options() + ("bob.enabled", value()->default_value(false), "Enable or disable BOB command channel") + ("bob.address", value()->default_value("127.0.0.1"), "BOB listen address") + ("bob.port", value()->default_value(2827), "BOB listen port"); - options_description i2cp("I2CP options"); - i2cp.add_options() - ("i2cp.enabled", value()->default_value(false), "Enable or disable I2CP") - ("i2cp.address", value()->default_value("127.0.0.1"), "I2CP listen address") - ("i2cp.port", value()->default_value(7654), "I2CP listen port") - ("i2cp.singlethread", value()->default_value(true), "Destinations run in the I2CP server's thread") - ; + options_description i2cp("I2CP options"); + i2cp.add_options() + ("i2cp.enabled", value()->default_value(false), "Enable or disable I2CP") + ("i2cp.address", value()->default_value("127.0.0.1"), "I2CP listen address") + ("i2cp.port", value()->default_value(7654), "I2CP listen port") + ("i2cp.singlethread", value()->default_value(true), + "Destinations run in the I2CP server's thread"); - options_description i2pcontrol("I2PControl options"); - i2pcontrol.add_options() - ("i2pcontrol.enabled", value()->default_value(false), "Enable or disable I2P Control Protocol") - ("i2pcontrol.address", value()->default_value("127.0.0.1"), "I2PCP listen address") - ("i2pcontrol.port", value()->default_value(7650), "I2PCP listen port") - ("i2pcontrol.password", value()->default_value("itoopie"), "I2PCP access password") - ("i2pcontrol.cert", value()->default_value("i2pcontrol.crt.pem"), "I2PCP connection certificate") - ("i2pcontrol.key", value()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key") - ; + options_description i2pcontrol("I2PControl options"); + i2pcontrol.add_options() + ("i2pcontrol.enabled", value()->default_value(false), + "Enable or disable I2P Control Protocol") + ("i2pcontrol.address", value()->default_value("127.0.0.1"), "I2PCP listen address") + ("i2pcontrol.port", value()->default_value(7650), "I2PCP listen port") + ("i2pcontrol.password", value()->default_value("itoopie"), "I2PCP access password") + ("i2pcontrol.cert", value()->default_value("i2pcontrol.crt.pem"), + "I2PCP connection certificate") + ("i2pcontrol.key", value()->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))) - 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 - options_description upnp("UPnP options"); - upnp.add_options() - ("upnp.enabled", value()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding") - ("upnp.name", value()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list") - ; + options_description upnp("UPnP options"); + upnp.add_options() + ("upnp.enabled", value()->default_value(upnp_default), + "Enable or disable UPnP: automatic port forwarding") + ("upnp.name", value()->default_value("I2Pd"), + "Name i2pd appears in UPnP forwarding list"); - options_description precomputation("Precomputation options"); - precomputation.add_options() - ("precomputation.elgamal", + options_description precomputation("Precomputation options"); + precomputation.add_options() + ("precomputation.elgamal", #if defined(__x86_64__) - value()->default_value(false), + value()->default_value(false), #else - value()->default_value(true), + value()->default_value(true), #endif - "Enable or disable elgamal precomputation table") - ; + "Enable or disable elgamal precomputation table"); - options_description reseed("Reseed options"); - reseed.add_options() - ("reseed.verify", value()->default_value(false), "Verify .su3 signature") - ("reseed.threshold", value()->default_value(25), "Minimum number of known routers before requesting reseed") - ("reseed.floodfill", value()->default_value(""), "Path to router info of floodfill to reseed from") - ("reseed.file", value()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") - ("reseed.zipfile", value()->default_value(""), "Path to local .zip file to reseed from") - ("reseed.proxy", value()->default_value(""), "url for reseed proxy, supports http/socks") - ("reseed.urls", value()->default_value( - "https://reseed2.i2p.net/," - "https://reseed.diva.exchange/," - "https://reseed-fr.i2pd.xyz/," - "https://reseed.memcpy.io/," - "https://reseed.onion.im/," - "https://i2pseed.creativecowpat.net:8443/," - "https://reseed.i2pgit.org/," - "https://i2p.novg.net/," - "https://banana.incognet.io/," - "https://reseed-pl.i2pd.xyz/," - "https://www2.mk16.de/" - ), "Reseed URLs, separated by comma") - ("reseed.yggurls", value()->default_value( - "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 reseed("Reseed options"); + reseed.add_options() + ("reseed.verify", value()->default_value(false), "Verify .su3 signature") + ("reseed.threshold", value()->default_value(25), + "Minimum number of known routers before requesting reseed") + ("reseed.floodfill", value()->default_value(""), + "Path to router info of floodfill to reseed from") + ("reseed.file", value()->default_value(""), + "Path to local .su3 file or HTTPS URL to reseed from") + ("reseed.zipfile", value()->default_value(""), + "Path to local .zip file to reseed from") + ("reseed.proxy", value()->default_value(""), + "url for reseed proxy, supports http/socks") + ("reseed.urls", value()->default_value( + "https://reseed2.i2p.net/," + "https://reseed.diva.exchange/," + "https://reseed-fr.i2pd.xyz/," + "https://reseed.memcpy.io/," + "https://reseed.onion.im/," + "https://i2pseed.creativecowpat.net:8443/," + "https://reseed.i2pgit.org/," + "https://i2p.novg.net/," + "https://banana.incognet.io/," + "https://reseed-pl.i2pd.xyz/," + "https://www2.mk16.de/" + ), "Reseed URLs, separated by comma") + ("reseed.yggurls", value()->default_value( + "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"); - addressbook.add_options() - ("addressbook.enabled", value()->default_value(true), "Enable address book lookups and subscritions (default: enabled)") - ("addressbook.defaulturl", value()->default_value( - "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" - ), "AddressBook subscription URL for initial setup") - ("addressbook.subscriptions", value()->default_value( - "http://reg.i2p/hosts.txt" - ), "AddressBook subscriptions URLs, separated by comma") - ("addressbook.hostsfile", value()->default_value(""), "File to dump addresses in hosts.txt format"); + options_description addressbook("AddressBook options"); + addressbook.add_options() + ("addressbook.enabled", value()->default_value(true), + "Enable address book lookups and subscritions (default: enabled)") + ("addressbook.defaulturl", value()->default_value( + "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" + ), "AddressBook subscription URL for initial setup") + ("addressbook.subscriptions", value()->default_value( + "http://reg.i2p/hosts.txt" + ), "AddressBook subscriptions URLs, separated by comma") + ("addressbook.hostsfile", value()->default_value(""), + "File to dump addresses in hosts.txt format"); - options_description trust("Trust options"); - trust.add_options() - ("trust.enabled", value()->default_value(false), "Enable explicit trust options") - ("trust.family", value()->default_value(""), "Router Family to trust for first hops") - ("trust.routers", value()->default_value(""), "Only Connect to these routers") - ("trust.hidden", value()->default_value(false), "Should we hide our router from other routers?") - ; + options_description trust("Trust options"); + trust.add_options() + ("trust.enabled", value()->default_value(false), "Enable explicit trust options") + ("trust.family", value()->default_value(""), "Router Family to trust for first hops") + ("trust.routers", value()->default_value(""), "Only Connect to these routers") + ("trust.hidden", value()->default_value(false), + "Should we hide our router from other routers?"); - // Save deprecated websocket options for compatibility - options_description websocket("Websocket Options"); - websocket.add_options() - ("websockets.enabled", value()->default_value(false), "Deprecated option") - ("websockets.address", value()->default_value(""), "Deprecated option") - ("websockets.port", value()->default_value(0), "Deprecated option") - ; + // Save deprecated websocket options for compatibility + options_description websocket("Websocket Options"); + websocket.add_options() + ("websockets.enabled", value()->default_value(false), "Deprecated option") + ("websockets.address", value()->default_value(""), "Deprecated option") + ("websockets.port", value()->default_value(0), "Deprecated option"); - options_description exploratory("Exploratory Options"); - exploratory.add_options() - ("exploratory.inbound.length", value()->default_value(2), "Exploratory inbound tunnel length") - ("exploratory.outbound.length", value()->default_value(2), "Exploratory outbound tunnel length") - ("exploratory.inbound.quantity", value()->default_value(3), "Exploratory inbound tunnels quantity") - ("exploratory.outbound.quantity", value()->default_value(3), "Exploratory outbound tunnels quantity") - ; + options_description exploratory("Exploratory Options"); + exploratory.add_options() + ("exploratory.inbound.length", value()->default_value(2), "Exploratory inbound tunnel length") + ("exploratory.outbound.length", value()->default_value(2), + "Exploratory outbound tunnel length") + ("exploratory.inbound.quantity", value()->default_value(3), + "Exploratory inbound tunnels quantity") + ("exploratory.outbound.quantity", value()->default_value(3), + "Exploratory outbound tunnels quantity"); - options_description ntcp2("NTCP2 Options"); - ntcp2.add_options() - ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") - ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") - ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") - ("ntcp2.addressv6", value()->default_value("::"), "Address to publish NTCP2 with") - ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") - ; + options_description ntcp2("NTCP2 Options"); + ntcp2.add_options() + ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") + ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") + ("ntcp2.port", value()->default_value(0), + "Port to listen for incoming NTCP2 connections (default: auto)") + ("ntcp2.addressv6", value()->default_value("::"), "Address to publish NTCP2 with") + ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport"); - options_description ssu2("SSU2 Options"); - ssu2.add_options() - ("ssu2.enabled", value()->default_value(false), "Enable SSU2 (default: disabled)") - ("ssu2.published", value()->default_value(false), "Publish SSU2 (default: disabled)") - ("ssu2.port", value()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)") - ; + options_description ssu2("SSU2 Options"); + ssu2.add_options() + ("ssu2.enabled", value()->default_value(false), "Enable SSU2 (default: disabled)") + ("ssu2.published", value()->default_value(false), "Publish SSU2 (default: disabled)") + ("ssu2.port", value()->default_value(0), + "Port to listen for incoming SSU2 packets (default: auto)"); - options_description nettime("Time sync options"); - nettime.add_options() - ("nettime.enabled", value()->default_value(false), "Disable time sync (default: disabled)") - ("nettime.ntpservers", value()->default_value( - "0.pool.ntp.org," - "1.pool.ntp.org," - "2.pool.ntp.org," - "3.pool.ntp.org" - ), "Comma separated list of NTP servers") - ("nettime.ntpsyncinterval", value()->default_value(72), "NTP sync interval in hours (default: 72)") - ("nettime.frompeers", value()->default_value(true), "Sync clock from transport peers (default: enabled)") - ; + options_description nettime("Time sync options"); + nettime.add_options() + ("nettime.enabled", value()->default_value(false), "Disable time sync (default: disabled)") + ("nettime.ntpservers", value()->default_value( + "0.pool.ntp.org," + "1.pool.ntp.org," + "2.pool.ntp.org," + "3.pool.ntp.org" + ), "Comma separated list of NTP servers") + ("nettime.ntpsyncinterval", value()->default_value(72), + "NTP sync interval in hours (default: 72)") + ("nettime.frompeers", value()->default_value(true), + "Sync clock from transport peers (default: enabled)"); - options_description persist("Network information persisting options"); - persist.add_options() - ("persist.profiles", value()->default_value(true), "Persist peer profiles (default: true)") - ("persist.addressbook", value()->default_value(true), "Persist full addresses (default: true)") - ; + options_description persist("Network information persisting options"); + persist.add_options() + ("persist.profiles", value()->default_value(true), "Persist peer profiles (default: true)") + ("persist.addressbook", value()->default_value(true), + "Persist full addresses (default: true)"); - options_description cpuext("CPU encryption extensions options"); - cpuext.add_options() - ("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used") - ("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used") - ("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines") - ; + options_description cpuext("CPU encryption extensions options"); + cpuext.add_options() + ("cpuext.aesni", bool_switch()->default_value(true), + "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used") + ("cpuext.avx", bool_switch()->default_value(true), + "Use auto detection for AVX CPU extensions. If false, AVX will be not used") + ("cpuext.force", bool_switch()->default_value(false), + "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines"); - options_description meshnets("Meshnet transports options"); - meshnets.add_options() - ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") - ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish") - ; + options_description meshnets("Meshnet transports options"); + meshnets.add_options() + ("meshnets.yggdrasil", bool_switch()->default_value(false), + "Support transports through the Yggdrasil (default: false)") + ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish"); #ifdef __linux__ - options_description unix_specific("UNIX-specific options"); - unix_specific.add_options() - ("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)") - ; + options_description unix_specific("UNIX-specific options"); + unix_specific.add_options() + ("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)") + ; #endif - m_OptionsDesc - .add(general) - .add(limits) - .add(httpserver) - .add(httpproxy) - .add(socksproxy) - .add(sam) - .add(bob) - .add(i2cp) - .add(i2pcontrol) - .add(upnp) - .add(precomputation) - .add(reseed) - .add(addressbook) - .add(trust) - .add(websocket) // deprecated - .add(exploratory) - .add(ntcp2) - .add(ssu2) - .add(nettime) - .add(persist) - .add(cpuext) - .add(meshnets) + m_OptionsDesc + .add(general) + .add(limits) + .add(httpserver) + .add(httpproxy) + .add(socksproxy) + .add(sam) + .add(bob) + .add(i2cp) + .add(i2pcontrol) + .add(upnp) + .add(precomputation) + .add(reseed) + .add(addressbook) + .add(trust) + .add(websocket) // deprecated + .add(exploratory) + .add(ntcp2) + .add(ssu2) + .add(nettime) + .add(persist) + .add(cpuext) + .add(meshnets) #ifdef __linux__ - .add(unix_specific) + .add(unix_specific) #endif - ; - } + ; + } - void ParseCmdline(int argc, char* argv[], bool ignoreUnknown) - { - try - { - auto style = boost::program_options::command_line_style::unix_style - | boost::program_options::command_line_style::allow_long_disguise; - style &= ~ boost::program_options::command_line_style::allow_guessing; - if (ignoreUnknown) - store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options); - else - store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options); - } - catch (boost::program_options::error& e) - { - ThrowFatal ("Error while parsing arguments: ", e.what()); - std::cerr << "args: " << e.what() << std::endl; - exit(EXIT_FAILURE); - } + void ParseCmdline(int argc, char *argv[], bool ignoreUnknown) { + try { + auto style = boost::program_options::command_line_style::unix_style + | boost::program_options::command_line_style::allow_long_disguise; + style &= ~boost::program_options::command_line_style::allow_guessing; + if (ignoreUnknown) + store(command_line_parser(argc, argv).options(m_OptionsDesc).style( + style).allow_unregistered().run(), m_Options); + else + store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options); + } + catch (boost::program_options::error &e) { + ThrowFatal("Error while parsing arguments: ", e.what()); + std::cerr << "args: " << e.what() << std::endl; + exit(EXIT_FAILURE); + } - if (!ignoreUnknown && (m_Options.count("help") || m_Options.count("h"))) - { - std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; - std::cout << m_OptionsDesc; - exit(EXIT_SUCCESS); - } - else if (m_Options.count("version")) - { - std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; - std::cout << "Boost version " - << BOOST_VERSION / 100000 << "." // maj. version - << BOOST_VERSION / 100 % 1000 << "." // min. version - << BOOST_VERSION % 100 // patch version - << std::endl; + if (!ignoreUnknown && (m_Options.count("help") || m_Options.count("h"))) { + std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; + std::cout << m_OptionsDesc; + exit(EXIT_SUCCESS); + } else if (m_Options.count("version")) { + std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; + std::cout << "Boost version " + << BOOST_VERSION / 100000 << "." // maj. version + << BOOST_VERSION / 100 % 1000 << "." // min. version + << BOOST_VERSION % 100 // patch version + << std::endl; #if defined(OPENSSL_VERSION_TEXT) - std::cout << OPENSSL_VERSION_TEXT << std::endl; + std::cout << OPENSSL_VERSION_TEXT << std::endl; #endif #if defined(LIBRESSL_VERSION_TEXT) - std::cout << LIBRESSL_VERSION_TEXT << std::endl; + std::cout << LIBRESSL_VERSION_TEXT << std::endl; #endif - exit(EXIT_SUCCESS); - } - } + exit(EXIT_SUCCESS); + } + } - void ParseConfig(const std::string& path) - { - if (path == "") return; + void ParseConfig(const std::string &path) { + if (path == "") return; - std::ifstream config(path, std::ios::in); + std::ifstream config(path, std::ios::in); - if (!config.is_open()) - { - ThrowFatal ("Missing or unreadable config file: ", path); - std::cerr << "missing/unreadable config file: " << path << std::endl; - exit(EXIT_FAILURE); - } + if (!config.is_open()) { + ThrowFatal("Missing or unreadable config file: ", path); + std::cerr << "missing/unreadable config file: " << path << std::endl; + exit(EXIT_FAILURE); + } - try - { - store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options); - } - catch (boost::program_options::error& e) - { - ThrowFatal ("Error while parsing config file: ", e.what()); - std::cerr << e.what() << std::endl; - exit(EXIT_FAILURE); - }; - } + try { + store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options); + } + catch (boost::program_options::error &e) { + ThrowFatal("Error while parsing config file: ", e.what()); + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); + }; + } - void Finalize() - { - notify(m_Options); - } + void Finalize() { + notify(m_Options); + } - bool IsDefault(const char *name) - { - if (!m_Options.count(name)) - throw "try to check non-existent option"; + bool IsDefault(const char *name) { + if (!m_Options.count(name)) + throw "try to check non-existent option"; - if (m_Options[name].defaulted()) - return true; - return false; - } + if (m_Options[name].defaulted()) + return true; + return false; + } - bool GetOptionAsAny(const char *name, boost::any& value) - { - if (!m_Options.count(name)) - return false; - value = m_Options[name]; - return true; - } + bool GetOptionAsAny(const char *name, boost::any &value) { + if (!m_Options.count(name)) + return false; + value = m_Options[name]; + return true; + } - bool GetOptionAsAny(const std::string& name, boost::any& value) - { - return GetOptionAsAny (name.c_str (), value); - } + bool GetOptionAsAny(const std::string &name, boost::any &value) { + return GetOptionAsAny(name.c_str(), value); + } -} // namespace config + } // namespace config } // namespace i2p diff --git a/libi2pd/Config.h b/libi2pd/Config.h index 79463e65..e3b6054e 100644 --- a/libi2pd/Config.h +++ b/libi2pd/Config.h @@ -25,103 +25,101 @@ */ namespace i2p { -namespace config { - extern boost::program_options::variables_map m_Options; + namespace config { + extern boost::program_options::variables_map m_Options; - /** - * @brief Initialize list of acceptable parameters - * - * Should be called before any Parse* functions. - */ - void Init(); + /** + * @brief Initialize list of acceptable parameters + * + * Should be called before any Parse* functions. + */ + void Init(); - /** - * @brief Parse cmdline parameters, and show help if requested - * @param argc Cmdline arguments count, should be passed from main(). - * @param argv Cmdline parameters array, should be passed from main() - * - * If --help is given in parameters, shows its list with description - * and terminates the program with exitcode 0. - * - * In case of parameter misuse boost throws an exception. - * We internally handle type boost::program_options::unknown_option, - * and then terminate the program with exitcode 1. - * - * Other exceptions will be passed to higher level. - */ - void ParseCmdline(int argc, char* argv[], bool ignoreUnknown = false); + /** + * @brief Parse cmdline parameters, and show help if requested + * @param argc Cmdline arguments count, should be passed from main(). + * @param argv Cmdline parameters array, should be passed from main() + * + * If --help is given in parameters, shows its list with description + * and terminates the program with exitcode 0. + * + * In case of parameter misuse boost throws an exception. + * We internally handle type boost::program_options::unknown_option, + * and then terminate the program with exitcode 1. + * + * Other exceptions will be passed to higher level. + */ + void ParseCmdline(int argc, char *argv[], bool ignoreUnknown = false); - /** - * @brief Load and parse given config file - * @param path Path to config file - * - * If error occurred when opening file path is points to, - * we show the error message and terminate program. - * - * In case of parameter misuse boost throws an exception. - * We internally handle type boost::program_options::unknown_option, - * and then terminate program with exitcode 1. - * - * Other exceptions will be passed to higher level. - */ - void ParseConfig(const std::string& path); + /** + * @brief Load and parse given config file + * @param path Path to config file + * + * If error occurred when opening file path is points to, + * we show the error message and terminate program. + * + * In case of parameter misuse boost throws an exception. + * We internally handle type boost::program_options::unknown_option, + * and then terminate program with exitcode 1. + * + * Other exceptions will be passed to higher level. + */ + void ParseConfig(const std::string &path); - /** - * @brief Used to combine options from cmdline, config and default values - */ - void Finalize(); + /** + * @brief Used to combine options from cmdline, config and default values + */ + void Finalize(); - /** - * @brief Accessor to parameters by name - * @param name Name of the requested parameter - * @param value Variable where to store option - * @return this function returns false if parameter not found - * - * Example: uint16_t port; GetOption("sam.port", port); - */ - template - bool GetOption(const char *name, T& value) - { - if (!m_Options.count(name)) - return false; - value = m_Options[name].as(); - return true; - } + /** + * @brief Accessor to parameters by name + * @param name Name of the requested parameter + * @param value Variable where to store option + * @return this function returns false if parameter not found + * + * Example: uint16_t port; GetOption("sam.port", port); + */ + template + bool GetOption(const char *name, T &value) { + if (!m_Options.count(name)) + return false; + value = m_Options[name].as(); + return true; + } - template - bool GetOption(const std::string& name, T& value) - { - return GetOption (name.c_str (), value); - } + template + bool GetOption(const std::string &name, T &value) { + return GetOption(name.c_str(), value); + } - bool GetOptionAsAny(const char *name, boost::any& value); - bool GetOptionAsAny(const std::string& name, boost::any& value); + bool GetOptionAsAny(const char *name, boost::any &value); - /** - * @brief Set value of given parameter - * @param name Name of settable parameter - * @param value New parameter value - * @return true if value set up successful, false otherwise - * - * Example: uint16_t port = 2827; SetOption("bob.port", port); - */ - template - bool SetOption(const char *name, const T& value) - { - if (!m_Options.count(name)) - return false; - m_Options.at(name).value() = value; - notify(m_Options); - return true; - } + bool GetOptionAsAny(const std::string &name, boost::any &value); - /** - * @brief Check is value explicitly given or default - * @param name Name of checked parameter - * @return true if value set to default, false otherwise - */ - bool IsDefault(const char *name); -} + /** + * @brief Set value of given parameter + * @param name Name of settable parameter + * @param value New parameter value + * @return true if value set up successful, false otherwise + * + * Example: uint16_t port = 2827; SetOption("bob.port", port); + */ + template + bool SetOption(const char *name, const T &value) { + if (!m_Options.count(name)) + return false; + m_Options.at(name).value() = value; + notify(m_Options); + return true; + } + + /** + * @brief Check is value explicitly given or default + * @param name Name of checked parameter + * @return true if value set to default, false otherwise + */ + bool IsDefault(const char *name); + } } #endif // CONFIG_H diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index d7fb965e..674beb86 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -16,1378 +16,1335 @@ #include #include "TunnelBase.h" #include + #if OPENSSL_HKDF #include #endif #if !OPENSSL_AEAD_CHACHA20_POLY1305 + #include "ChaCha20.h" #include "Poly1305.h" + #endif + #include "Crypto.h" #include "Ed25519.h" #include "I2PEndian.h" #include "Log.h" -namespace i2p -{ -namespace crypto -{ - const uint8_t elgp_[256]= - { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, - 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, - 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, - 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, - 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, - 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, - 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, - 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, - 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; +namespace i2p { + namespace crypto { + const uint8_t elgp_[256] = + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; - const int elgg_ = 2; + const int elgg_ = 2; - const uint8_t dsap_[128]= - { - 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, - 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, - 0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23, - 0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c, - 0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8, - 0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c, - 0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11, - 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 - }; + const uint8_t dsap_[128] = + { + 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, + 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, + 0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23, + 0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c, + 0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8, + 0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c, + 0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11, + 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 + }; - const uint8_t dsaq_[20]= - { - 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, - 0x68, 0x40, 0x46, 0xb7 - }; + const uint8_t dsaq_[20] = + { + 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, + 0x68, 0x40, 0x46, 0xb7 + }; - const uint8_t dsag_[128]= - { - 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, - 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, - 0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2, - 0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52, - 0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc, - 0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a, - 0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c, - 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 - }; + const uint8_t dsag_[128] = + { + 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, + 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, + 0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2, + 0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52, + 0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc, + 0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a, + 0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c, + 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 + }; - const int rsae_ = 65537; + const int rsae_ = 65537; - struct CryptoConstants - { - // DH/ElGamal - BIGNUM * elgp; - BIGNUM * elgg; + struct CryptoConstants { + // DH/ElGamal + BIGNUM *elgp; + BIGNUM *elgg; - // DSA - BIGNUM * dsap; - BIGNUM * dsaq; - BIGNUM * dsag; + // DSA + BIGNUM *dsap; + BIGNUM *dsaq; + BIGNUM *dsag; - // RSA - BIGNUM * rsae; + // RSA + BIGNUM *rsae; - CryptoConstants (const uint8_t * elgp_, int elgg_, const uint8_t * dsap_, - const uint8_t * dsaq_, const uint8_t * dsag_, int rsae_) - { - elgp = BN_new (); - BN_bin2bn (elgp_, 256, elgp); - elgg = BN_new (); - BN_set_word (elgg, elgg_); - dsap = BN_new (); - BN_bin2bn (dsap_, 128, dsap); - dsaq = BN_new (); - BN_bin2bn (dsaq_, 20, dsaq); - dsag = BN_new (); - BN_bin2bn (dsag_, 128, dsag); - rsae = BN_new (); - BN_set_word (rsae, rsae_); - } + CryptoConstants(const uint8_t *elgp_, int elgg_, const uint8_t *dsap_, + const uint8_t *dsaq_, const uint8_t *dsag_, int rsae_) { + elgp = BN_new(); + BN_bin2bn(elgp_, 256, elgp); + elgg = BN_new(); + BN_set_word(elgg, elgg_); + dsap = BN_new(); + BN_bin2bn(dsap_, 128, dsap); + dsaq = BN_new(); + BN_bin2bn(dsaq_, 20, dsaq); + dsag = BN_new(); + BN_bin2bn(dsag_, 128, dsag); + rsae = BN_new(); + BN_set_word(rsae, rsae_); + } - ~CryptoConstants () - { - BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae); - } - }; + ~CryptoConstants() { + BN_free(elgp); + BN_free(elgg); + BN_free(dsap); + BN_free(dsaq); + BN_free(dsag); + BN_free(rsae); + } + }; - static const CryptoConstants& GetCryptoConstants () - { - static CryptoConstants cryptoConstants (elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_); - return cryptoConstants; - } + static const CryptoConstants &GetCryptoConstants() { + static CryptoConstants cryptoConstants(elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_); + return cryptoConstants; + } - bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len) - { - int offset = len - BN_num_bytes (bn); - if (offset < 0) return false; - BN_bn2bin (bn, buf + offset); - memset (buf, 0, offset); - return true; - } + bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len) { + int offset = len - BN_num_bytes(bn); + if (offset < 0) return false; + BN_bn2bin(bn, buf + offset); + memset(buf, 0, offset); + return true; + } // RSA - #define rsae GetCryptoConstants ().rsae - const BIGNUM * GetRSAE () - { - return rsae; - } +#define rsae GetCryptoConstants ().rsae + + const BIGNUM *GetRSAE() { + return rsae; + } // DSA - #define dsap GetCryptoConstants ().dsap - #define dsaq GetCryptoConstants ().dsaq - #define dsag GetCryptoConstants ().dsag - DSA * CreateDSA () - { - DSA * dsa = DSA_new (); - DSA_set0_pqg (dsa, BN_dup (dsap), BN_dup (dsaq), BN_dup (dsag)); - DSA_set0_key (dsa, NULL, NULL); - return dsa; - } +#define dsap GetCryptoConstants ().dsap +#define dsaq GetCryptoConstants ().dsaq +#define dsag GetCryptoConstants ().dsag + + DSA *CreateDSA() { + DSA *dsa = DSA_new(); + DSA_set0_pqg(dsa, BN_dup(dsap), BN_dup(dsaq), BN_dup(dsag)); + DSA_set0_key(dsa, NULL, NULL); + return dsa; + } // DH/ElGamal - const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226; - const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1; - const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048; - const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8; + const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226; + const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS / 8 + 1; + const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048; + const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS / 8; - #define elgp GetCryptoConstants ().elgp - #define elgg GetCryptoConstants ().elgg +#define elgp GetCryptoConstants ().elgp +#define elgg GetCryptoConstants ().elgg - static BN_MONT_CTX * g_MontCtx = nullptr; - static void PrecalculateElggTable (BIGNUM * table[][255], int len) // table is len's array of array of 255 bignums - { - if (len <= 0) return; - BN_CTX * ctx = BN_CTX_new (); - g_MontCtx = BN_MONT_CTX_new (); - BN_MONT_CTX_set (g_MontCtx, elgp, ctx); - auto montCtx = BN_MONT_CTX_new (); - BN_MONT_CTX_copy (montCtx, g_MontCtx); - for (int i = 0; i < len; i++) - { - table[i][0] = BN_new (); - if (!i) - BN_to_montgomery (table[0][0], elgg, montCtx, ctx); - else - BN_mod_mul_montgomery (table[i][0], table[i-1][254], table[i-1][0], montCtx, ctx); - for (int j = 1; j < 255; j++) - { - table[i][j] = BN_new (); - BN_mod_mul_montgomery (table[i][j], table[i][j-1], table[i][0], montCtx, ctx); - } - } - BN_MONT_CTX_free (montCtx); - BN_CTX_free (ctx); - } + static BN_MONT_CTX *g_MontCtx = nullptr; - static void DestroyElggTable (BIGNUM * table[][255], int len) - { - for (int i = 0; i < len; i++) - for (int j = 0; j < 255; j++) - { - BN_free (table[i][j]); - table[i][j] = nullptr; - } - BN_MONT_CTX_free (g_MontCtx); - } + static void PrecalculateElggTable(BIGNUM *table[][255], int len) // table is len's array of array of 255 bignums + { + if (len <= 0) return; + BN_CTX *ctx = BN_CTX_new(); + g_MontCtx = BN_MONT_CTX_new(); + BN_MONT_CTX_set(g_MontCtx, elgp, ctx); + auto montCtx = BN_MONT_CTX_new(); + BN_MONT_CTX_copy(montCtx, g_MontCtx); + for (int i = 0; i < len; i++) { + table[i][0] = BN_new(); + if (!i) + BN_to_montgomery(table[0][0], elgg, montCtx, ctx); + else + BN_mod_mul_montgomery(table[i][0], table[i - 1][254], table[i - 1][0], montCtx, ctx); + for (int j = 1; j < 255; j++) { + table[i][j] = BN_new(); + BN_mod_mul_montgomery(table[i][j], table[i][j - 1], table[i][0], montCtx, ctx); + } + } + BN_MONT_CTX_free(montCtx); + BN_CTX_free(ctx); + } - static BIGNUM * ElggPow (const uint8_t * exp, int len, BIGNUM * table[][255], BN_CTX * ctx) - // exp is in Big Endian - { - if (len <= 0) return nullptr; - auto montCtx = BN_MONT_CTX_new (); - BN_MONT_CTX_copy (montCtx, g_MontCtx); - BIGNUM * res = nullptr; - for (int i = 0; i < len; i++) - { - if (res) - { - if (exp[i]) - BN_mod_mul_montgomery (res, res, table[len-1-i][exp[i]-1], montCtx, ctx); - } - else if (exp[i]) - res = BN_dup (table[len-i-1][exp[i]-1]); - } - if (res) - BN_from_montgomery (res, res, montCtx, ctx); - BN_MONT_CTX_free (montCtx); - return res; - } + static void DestroyElggTable(BIGNUM *table[][255], int len) { + for (int i = 0; i < len; i++) + for (int j = 0; j < 255; j++) { + BN_free(table[i][j]); + table[i][j] = nullptr; + } + BN_MONT_CTX_free(g_MontCtx); + } - static BIGNUM * ElggPow (const BIGNUM * exp, BIGNUM * table[][255], BN_CTX * ctx) - { - auto len = BN_num_bytes (exp); - uint8_t * buf = new uint8_t[len]; - BN_bn2bin (exp, buf); - auto ret = ElggPow (buf, len, table, ctx); - delete[] buf; - return ret; - } + static BIGNUM *ElggPow(const uint8_t *exp, int len, BIGNUM *table[][255], BN_CTX *ctx) + // exp is in Big Endian + { + if (len <= 0) return nullptr; + auto montCtx = BN_MONT_CTX_new(); + BN_MONT_CTX_copy(montCtx, g_MontCtx); + BIGNUM *res = nullptr; + for (int i = 0; i < len; i++) { + if (res) { + if (exp[i]) + BN_mod_mul_montgomery(res, res, table[len - 1 - i][exp[i] - 1], montCtx, ctx); + } else if (exp[i]) + res = BN_dup(table[len - i - 1][exp[i] - 1]); + } + if (res) + BN_from_montgomery(res, res, montCtx, ctx); + BN_MONT_CTX_free(montCtx); + return res; + } - static BIGNUM * (* g_ElggTable)[255] = nullptr; + static BIGNUM *ElggPow(const BIGNUM *exp, BIGNUM *table[][255], BN_CTX *ctx) { + auto len = BN_num_bytes(exp); + uint8_t *buf = new uint8_t[len]; + BN_bn2bin(exp, buf); + auto ret = ElggPow(buf, len, table, ctx); + delete[] buf; + return ret; + } + + static BIGNUM *(*g_ElggTable)[255] = nullptr; // DH - DHKeys::DHKeys () - { - m_DH = DH_new (); - DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg)); - DH_set0_key (m_DH, NULL, NULL); - } + DHKeys::DHKeys() { + m_DH = DH_new(); + DH_set0_pqg(m_DH, BN_dup(elgp), NULL, BN_dup(elgg)); + DH_set0_key(m_DH, NULL, NULL); + } - DHKeys::~DHKeys () - { - DH_free (m_DH); - } + DHKeys::~DHKeys() { + DH_free(m_DH); + } - void DHKeys::GenerateKeys () - { - BIGNUM * priv_key = NULL, * pub_key = NULL; + void DHKeys::GenerateKeys() { + BIGNUM *priv_key = NULL, *pub_key = NULL; #if !defined(__x86_64__) // use short exponent for non x64 - priv_key = BN_new (); - BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1); + priv_key = BN_new(); + BN_rand(priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1); #endif - if (g_ElggTable) - { + if (g_ElggTable) { #if defined(__x86_64__) - priv_key = BN_new (); - BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1); + priv_key = BN_new (); + BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1); #endif - auto ctx = BN_CTX_new (); - pub_key = ElggPow (priv_key, g_ElggTable, ctx); - DH_set0_key (m_DH, pub_key, priv_key); - BN_CTX_free (ctx); - } - else - { - DH_set0_key (m_DH, NULL, priv_key); - DH_generate_key (m_DH); - DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key); - } + auto ctx = BN_CTX_new(); + pub_key = ElggPow(priv_key, g_ElggTable, ctx); + DH_set0_key(m_DH, pub_key, priv_key); + BN_CTX_free(ctx); + } else { + DH_set0_key(m_DH, NULL, priv_key); + DH_generate_key(m_DH); + DH_get0_key(m_DH, (const BIGNUM **) &pub_key, (const BIGNUM **) &priv_key); + } - bn2buf (pub_key, m_PublicKey, 256); - } + bn2buf(pub_key, m_PublicKey, 256); + } - void DHKeys::Agree (const uint8_t * pub, uint8_t * shared) - { - BIGNUM * pk = BN_bin2bn (pub, 256, NULL); - DH_compute_key (shared, pk, m_DH); - BN_free (pk); - } + void DHKeys::Agree(const uint8_t *pub, uint8_t *shared) { + BIGNUM *pk = BN_bin2bn(pub, 256, NULL); + DH_compute_key(shared, pk, m_DH); + BN_free(pk); + } // x25519 - X25519Keys::X25519Keys () - { + X25519Keys::X25519Keys() { #if OPENSSL_X25519 - m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL); - m_Pkey = nullptr; + m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL); + m_Pkey = nullptr; #else - m_Ctx = BN_CTX_new (); + m_Ctx = BN_CTX_new(); #endif - } + } - X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub) - { + X25519Keys::X25519Keys(const uint8_t *priv, const uint8_t *pub) { #if OPENSSL_X25519 - m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); - m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); - if (pub) - memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey - else - { - size_t len = 32; - EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); - } + m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); + m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); + if (pub) + memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey + else + { + size_t len = 32; + EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); + } #else - m_Ctx = BN_CTX_new (); - memcpy (m_PrivateKey, priv, 32); - if (pub) - memcpy (m_PublicKey, pub, 32); - else - GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); + m_Ctx = BN_CTX_new(); + memcpy(m_PrivateKey, priv, 32); + if (pub) + memcpy(m_PublicKey, pub, 32); + else + GetEd25519()->ScalarMulB(m_PrivateKey, m_PublicKey, m_Ctx); #endif - } + } - X25519Keys::~X25519Keys () - { + X25519Keys::~X25519Keys() { #if OPENSSL_X25519 - EVP_PKEY_CTX_free (m_Ctx); - if (m_Pkey) EVP_PKEY_free (m_Pkey); + EVP_PKEY_CTX_free (m_Ctx); + if (m_Pkey) EVP_PKEY_free (m_Pkey); #else - BN_CTX_free (m_Ctx); + BN_CTX_free(m_Ctx); #endif - } + } - void X25519Keys::GenerateKeys () - { + void X25519Keys::GenerateKeys() { #if OPENSSL_X25519 - if (m_Pkey) - { - EVP_PKEY_free (m_Pkey); - m_Pkey = nullptr; - } - EVP_PKEY_keygen_init (m_Ctx); - EVP_PKEY_keygen (m_Ctx, &m_Pkey); - EVP_PKEY_CTX_free (m_Ctx); - m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx? - size_t len = 32; - EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); + if (m_Pkey) + { + EVP_PKEY_free (m_Pkey); + m_Pkey = nullptr; + } + EVP_PKEY_keygen_init (m_Ctx); + EVP_PKEY_keygen (m_Ctx, &m_Pkey); + EVP_PKEY_CTX_free (m_Ctx); + m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx? + size_t len = 32; + EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); #else - RAND_bytes (m_PrivateKey, 32); - GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); + RAND_bytes(m_PrivateKey, 32); + GetEd25519()->ScalarMulB(m_PrivateKey, m_PublicKey, m_Ctx); #endif - } + } - bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared) - { - if (!pub || (pub[31] & 0x80)) return false; // not x25519 key + bool X25519Keys::Agree(const uint8_t *pub, uint8_t *shared) { + if (!pub || (pub[31] & 0x80)) return false; // not x25519 key #if OPENSSL_X25519 - EVP_PKEY_derive_init (m_Ctx); - auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32); - if (!pkey) return false; - EVP_PKEY_derive_set_peer (m_Ctx, pkey); - size_t len = 32; - EVP_PKEY_derive (m_Ctx, shared, &len); - EVP_PKEY_free (pkey); + EVP_PKEY_derive_init (m_Ctx); + auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32); + if (!pkey) return false; + EVP_PKEY_derive_set_peer (m_Ctx, pkey); + size_t len = 32; + EVP_PKEY_derive (m_Ctx, shared, &len); + EVP_PKEY_free (pkey); #else - GetEd25519 ()->ScalarMul (pub, m_PrivateKey, shared, m_Ctx); + GetEd25519()->ScalarMul(pub, m_PrivateKey, shared, m_Ctx); #endif - return true; - } + return true; + } - void X25519Keys::GetPrivateKey (uint8_t * priv) const - { + void X25519Keys::GetPrivateKey(uint8_t *priv) const { #if OPENSSL_X25519 - size_t len = 32; - EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len); + size_t len = 32; + EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len); #else - memcpy (priv, m_PrivateKey, 32); + memcpy(priv, m_PrivateKey, 32); #endif - } + } - void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic) - { + void X25519Keys::SetPrivateKey(const uint8_t *priv, bool calculatePublic) { #if OPENSSL_X25519 - if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); - if (m_Pkey) EVP_PKEY_free (m_Pkey); - m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); - m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); - if (calculatePublic) - { - size_t len = 32; - EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); - } + if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); + if (m_Pkey) EVP_PKEY_free (m_Pkey); + m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); + m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); + if (calculatePublic) + { + size_t len = 32; + EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); + } #else - memcpy (m_PrivateKey, priv, 32); - if (calculatePublic) - GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); + memcpy(m_PrivateKey, priv, 32); + if (calculatePublic) + GetEd25519()->ScalarMulB(m_PrivateKey, m_PublicKey, m_Ctx); #endif - } + } // ElGamal - void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - // everything, but a, because a might come from table - BIGNUM * k = BN_CTX_get (ctx); - BIGNUM * y = BN_CTX_get (ctx); - BIGNUM * b1 = BN_CTX_get (ctx); - BIGNUM * b = BN_CTX_get (ctx); - // select random k + void ElGamalEncrypt(const uint8_t *key, const uint8_t *data, uint8_t *encrypted) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + // everything, but a, because a might come from table + BIGNUM *k = BN_CTX_get(ctx); + BIGNUM *y = BN_CTX_get(ctx); + BIGNUM *b1 = BN_CTX_get(ctx); + BIGNUM *b = BN_CTX_get(ctx); + // select random k #if defined(__x86_64__) - BN_rand (k, ELGAMAL_FULL_EXPONENT_NUM_BITS, -1, 1); // full exponent for x64 + BN_rand (k, ELGAMAL_FULL_EXPONENT_NUM_BITS, -1, 1); // full exponent for x64 #else - BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits + BN_rand(k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits #endif - // calculate a - BIGNUM * a; - if (g_ElggTable) - a = ElggPow (k, g_ElggTable, ctx); - else - { - a = BN_new (); - BN_mod_exp (a, elgg, k, elgp, ctx); - } + // calculate a + BIGNUM *a; + if (g_ElggTable) + a = ElggPow(k, g_ElggTable, ctx); + else { + a = BN_new(); + BN_mod_exp(a, elgg, k, elgp, ctx); + } - // restore y from key - BN_bin2bn (key, 256, y); - // calculate b1 - BN_mod_exp (b1, y, k, elgp, ctx); - // create m - uint8_t m[255]; - m[0] = 0xFF; - memcpy (m+33, data, 222); - SHA256 (m+33, 222, m+1); - // calculate b = b1*m mod p - BN_bin2bn (m, 255, b); - BN_mod_mul (b, b1, b, elgp, ctx); - // copy a and b - encrypted[0] = 0; - bn2buf (a, encrypted + 1, 256); - encrypted[257] = 0; - bn2buf (b, encrypted + 258, 256); + // restore y from key + BN_bin2bn(key, 256, y); + // calculate b1 + BN_mod_exp(b1, y, k, elgp, ctx); + // create m + uint8_t m[255]; + m[0] = 0xFF; + memcpy(m + 33, data, 222); + SHA256(m + 33, 222, m + 1); + // calculate b = b1*m mod p + BN_bin2bn(m, 255, b); + BN_mod_mul(b, b1, b, elgp, ctx); + // copy a and b + encrypted[0] = 0; + bn2buf(a, encrypted + 1, 256); + encrypted[257] = 0; + bn2buf(b, encrypted + 258, 256); - BN_free (a); - BN_CTX_end (ctx); - BN_CTX_free (ctx); - } + BN_free(a); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } - bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * x = BN_CTX_get (ctx), * a = BN_CTX_get (ctx), * b = BN_CTX_get (ctx); - BN_bin2bn (key, 256, x); - BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1 - BN_bin2bn (encrypted + 1, 256, a); - BN_bin2bn (encrypted + 258, 256, b); - // m = b*(a^x mod p) mod p - BN_mod_exp (x, a, x, elgp, ctx); - BN_mod_mul (b, b, x, elgp, ctx); - uint8_t m[255]; - bn2buf (b, m, 255); - BN_CTX_end (ctx); - BN_CTX_free (ctx); - uint8_t hash[32]; - SHA256 (m + 33, 222, hash); - if (memcmp (m + 1, hash, 32)) - { - LogPrint (eLogError, "ElGamal decrypt hash doesn't match"); - return false; - } - memcpy (data, m + 33, 222); - return true; - } + bool ElGamalDecrypt(const uint8_t *key, const uint8_t *encrypted, uint8_t *data) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *x = BN_CTX_get(ctx), *a = BN_CTX_get(ctx), *b = BN_CTX_get(ctx); + BN_bin2bn(key, 256, x); + BN_sub(x, elgp, x); + BN_sub_word(x, 1); // x = elgp - x- 1 + BN_bin2bn(encrypted + 1, 256, a); + BN_bin2bn(encrypted + 258, 256, b); + // m = b*(a^x mod p) mod p + BN_mod_exp(x, a, x, elgp, ctx); + BN_mod_mul(b, b, x, elgp, ctx); + uint8_t m[255]; + bn2buf(b, m, 255); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + uint8_t hash[32]; + SHA256(m + 33, 222, hash); + if (memcmp(m + 1, hash, 32)) { + LogPrint(eLogError, "ElGamal decrypt hash doesn't match"); + return false; + } + memcpy(data, m + 33, 222); + return true; + } - void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub) - { + void GenerateElGamalKeyPair(uint8_t *priv, uint8_t *pub) { #if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) - RAND_bytes (priv, 256); + RAND_bytes (priv, 256); #else - // lower 226 bits (28 bytes and 2 bits) only. short exponent - auto numBytes = (ELGAMAL_SHORT_EXPONENT_NUM_BITS)/8 + 1; // 29 - auto numZeroBytes = 256 - numBytes; - RAND_bytes (priv + numZeroBytes, numBytes); - memset (priv, 0, numZeroBytes); - priv[numZeroBytes] &= 0x03; + // lower 226 bits (28 bytes and 2 bits) only. short exponent + auto numBytes = (ELGAMAL_SHORT_EXPONENT_NUM_BITS) / 8 + 1; // 29 + auto numZeroBytes = 256 - numBytes; + RAND_bytes(priv + numZeroBytes, numBytes); + memset(priv, 0, numZeroBytes); + priv[numZeroBytes] &= 0x03; #endif - BN_CTX * ctx = BN_CTX_new (); - BIGNUM * p = BN_new (); - BN_bin2bn (priv, 256, p); - BN_mod_exp (p, elgg, p, elgp, ctx); - bn2buf (p, pub, 256); - BN_free (p); - BN_CTX_free (ctx); - } + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *p = BN_new(); + BN_bin2bn(priv, 256, p); + BN_mod_exp(p, elgg, p, elgp, ctx); + bn2buf(p, pub, 256); + BN_free(p); + BN_CTX_free(ctx); + } // ECIES - void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order(curve, q, ctx); - int len = BN_num_bytes (q); - BIGNUM * k = BN_CTX_get (ctx); - BN_rand_range (k, q); // 0 < k < q - // point for shared secret - auto p = EC_POINT_new (curve); - EC_POINT_mul (curve, p, k, nullptr, nullptr, ctx); - BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); - EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); - encrypted[0] = 0; - bn2buf (x, encrypted + 1, len); - bn2buf (y, encrypted + 1 + len, len); - RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); - // encryption key and iv - EC_POINT_mul (curve, p, nullptr, key, k, ctx); - EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); - uint8_t keyBuf[64], iv[64], shared[32]; - bn2buf (x, keyBuf, len); - bn2buf (y, iv, len); - SHA256 (keyBuf, len, shared); - // create buffer - uint8_t m[256]; - m[0] = 0xFF; m[255] = 0xFF; - memcpy (m+33, data, 222); - SHA256 (m+33, 222, m+1); - // encrypt - CBCEncryption encryption; - encryption.SetKey (shared); - encryption.SetIV (iv); - encrypted[257] = 0; - encryption.Encrypt (m, 256, encrypted + 258); - EC_POINT_free (p); - BN_CTX_end (ctx); - BN_CTX_free (ctx); - } + void ECIESEncrypt(const EC_GROUP *curve, const EC_POINT *key, const uint8_t *data, uint8_t *encrypted) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(curve, q, ctx); + int len = BN_num_bytes(q); + BIGNUM *k = BN_CTX_get(ctx); + BN_rand_range(k, q); // 0 < k < q + // point for shared secret + auto p = EC_POINT_new(curve); + EC_POINT_mul(curve, p, k, nullptr, nullptr, ctx); + BIGNUM *x = BN_CTX_get(ctx), *y = BN_CTX_get(ctx); + EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, nullptr); + encrypted[0] = 0; + bn2buf(x, encrypted + 1, len); + bn2buf(y, encrypted + 1 + len, len); + RAND_bytes(encrypted + 1 + 2 * len, 256 - 2 * len); + // encryption key and iv + EC_POINT_mul(curve, p, nullptr, key, k, ctx); + EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, nullptr); + uint8_t keyBuf[64], iv[64], shared[32]; + bn2buf(x, keyBuf, len); + bn2buf(y, iv, len); + SHA256(keyBuf, len, shared); + // create buffer + uint8_t m[256]; + m[0] = 0xFF; + m[255] = 0xFF; + memcpy(m + 33, data, 222); + SHA256(m + 33, 222, m + 1); + // encrypt + CBCEncryption encryption; + encryption.SetKey(shared); + encryption.SetIV(iv); + encrypted[257] = 0; + encryption.Encrypt(m, 256, encrypted + 258); + EC_POINT_free(p); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } - bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data) - { - bool ret = true; - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order(curve, q, ctx); - int len = BN_num_bytes (q); - // point for shared secret - BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); - BN_bin2bn (encrypted + 1, len, x); - BN_bin2bn (encrypted + 1 + len, len, y); - auto p = EC_POINT_new (curve); - if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr)) - { - auto s = EC_POINT_new (curve); - EC_POINT_mul (curve, s, nullptr, p, key, ctx); - EC_POINT_get_affine_coordinates_GFp (curve, s, x, y, nullptr); - EC_POINT_free (s); - uint8_t keyBuf[64], iv[64], shared[32]; - bn2buf (x, keyBuf, len); - bn2buf (y, iv, len); - SHA256 (keyBuf, len, shared); - // decrypt - uint8_t m[256]; - CBCDecryption decryption; - decryption.SetKey (shared); - decryption.SetIV (iv); - decryption.Decrypt (encrypted + 258, 256, m); - // verify and copy - uint8_t hash[32]; - SHA256 (m + 33, 222, hash); - if (!memcmp (m + 1, hash, 32)) - memcpy (data, m + 33, 222); - else - { - LogPrint (eLogError, "ECIES decrypt hash doesn't match"); - ret = false; - } - } - else - { - LogPrint (eLogError, "ECIES decrypt point is invalid"); - ret = false; - } + bool ECIESDecrypt(const EC_GROUP *curve, const BIGNUM *key, const uint8_t *encrypted, uint8_t *data) { + bool ret = true; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(curve, q, ctx); + int len = BN_num_bytes(q); + // point for shared secret + BIGNUM *x = BN_CTX_get(ctx), *y = BN_CTX_get(ctx); + BN_bin2bn(encrypted + 1, len, x); + BN_bin2bn(encrypted + 1 + len, len, y); + auto p = EC_POINT_new(curve); + if (EC_POINT_set_affine_coordinates_GFp(curve, p, x, y, nullptr)) { + auto s = EC_POINT_new(curve); + EC_POINT_mul(curve, s, nullptr, p, key, ctx); + EC_POINT_get_affine_coordinates_GFp(curve, s, x, y, nullptr); + EC_POINT_free(s); + uint8_t keyBuf[64], iv[64], shared[32]; + bn2buf(x, keyBuf, len); + bn2buf(y, iv, len); + SHA256(keyBuf, len, shared); + // decrypt + uint8_t m[256]; + CBCDecryption decryption; + decryption.SetKey(shared); + decryption.SetIV(iv); + decryption.Decrypt(encrypted + 258, 256, m); + // verify and copy + uint8_t hash[32]; + SHA256(m + 33, 222, hash); + if (!memcmp(m + 1, hash, 32)) + memcpy(data, m + 33, 222); + else { + LogPrint(eLogError, "ECIES decrypt hash doesn't match"); + ret = false; + } + } else { + LogPrint(eLogError, "ECIES decrypt point is invalid"); + ret = false; + } - EC_POINT_free (p); - BN_CTX_end (ctx); - BN_CTX_free (ctx); - return ret; - } + EC_POINT_free(p); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; + } - void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub) - { - BN_CTX * ctx = BN_CTX_new (); - BIGNUM * q = BN_new (); - EC_GROUP_get_order(curve, q, ctx); - priv = BN_new (); - BN_rand_range (priv, q); - pub = EC_POINT_new (curve); - EC_POINT_mul (curve, pub, priv, nullptr, nullptr, ctx); - BN_free (q); - BN_CTX_free (ctx); - } + void GenerateECIESKeyPair(const EC_GROUP *curve, BIGNUM *&priv, EC_POINT *&pub) { + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *q = BN_new(); + EC_GROUP_get_order(curve, q, ctx); + priv = BN_new(); + BN_rand_range(priv, q); + pub = EC_POINT_new(curve); + EC_POINT_mul(curve, pub, priv, nullptr, nullptr, ctx); + BN_free(q); + BN_CTX_free(ctx); + } // HMAC - const uint64_t IPAD = 0x3636363636363636; - const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; + const uint64_t IPAD = 0x3636363636363636; + const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; - static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD }; - static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD }; + static const uint64_t ipads[] = {IPAD, IPAD, IPAD, IPAD}; + static const uint64_t opads[] = {OPAD, OPAD, OPAD, OPAD}; - void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest) - // key is 32 bytes - // digest is 16 bytes - // block size is 64 bytes - { - uint64_t buf[256]; - uint64_t hash[12]; // 96 bytes + void HMACMD5Digest(uint8_t *msg, size_t len, const MACKey &key, uint8_t *digest) + // key is 32 bytes + // digest is 16 bytes + // block size is 64 bytes + { + uint64_t buf[256]; + uint64_t hash[12]; // 96 bytes #if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600) - if(i2p::cpu::avx) - { - __asm__ - ( - "vmovups %[key], %%ymm0 \n" - "vmovups %[ipad], %%ymm1 \n" - "vmovups %%ymm1, 32(%[buf]) \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, (%[buf]) \n" - "vmovups %[opad], %%ymm1 \n" - "vmovups %%ymm1, 32(%[hash]) \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, (%[hash]) \n" - "vzeroall \n" // end of AVX - "movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes - : - : [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads), - [buf]"r"(buf), [hash]"r"(hash) - : "memory", "%xmm0" // TODO: change to %ymm0 later - ); - } - else + if(i2p::cpu::avx) + { + __asm__ + ( + "vmovups %[key], %%ymm0 \n" + "vmovups %[ipad], %%ymm1 \n" + "vmovups %%ymm1, 32(%[buf]) \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, (%[buf]) \n" + "vmovups %[opad], %%ymm1 \n" + "vmovups %%ymm1, 32(%[hash]) \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, (%[hash]) \n" + "vzeroall \n" // end of AVX + "movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes + : + : [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads), + [buf]"r"(buf), [hash]"r"(hash) + : "memory", "%xmm0" // TODO: change to %ymm0 later + ); + } + else #endif - { - // ikeypad - buf[0] = key.GetLL ()[0] ^ IPAD; - buf[1] = key.GetLL ()[1] ^ IPAD; - buf[2] = key.GetLL ()[2] ^ IPAD; - buf[3] = key.GetLL ()[3] ^ IPAD; - buf[4] = IPAD; - buf[5] = IPAD; - buf[6] = IPAD; - buf[7] = IPAD; - // okeypad - hash[0] = key.GetLL ()[0] ^ OPAD; - hash[1] = key.GetLL ()[1] ^ OPAD; - hash[2] = key.GetLL ()[2] ^ OPAD; - hash[3] = key.GetLL ()[3] ^ OPAD; - hash[4] = OPAD; - hash[5] = OPAD; - hash[6] = OPAD; - hash[7] = OPAD; - // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (hash + 10, 0, 16); - } + { + // ikeypad + buf[0] = key.GetLL()[0] ^ IPAD; + buf[1] = key.GetLL()[1] ^ IPAD; + buf[2] = key.GetLL()[2] ^ IPAD; + buf[3] = key.GetLL()[3] ^ IPAD; + buf[4] = IPAD; + buf[5] = IPAD; + buf[6] = IPAD; + buf[7] = IPAD; + // okeypad + hash[0] = key.GetLL()[0] ^ OPAD; + hash[1] = key.GetLL()[1] ^ OPAD; + hash[2] = key.GetLL()[2] ^ OPAD; + hash[3] = key.GetLL()[3] ^ OPAD; + hash[4] = OPAD; + hash[5] = OPAD; + hash[6] = OPAD; + hash[7] = OPAD; + // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) + memset(hash + 10, 0, 16); + } - // concatenate with msg - memcpy (buf + 8, msg, len); - // calculate first hash - MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes + // concatenate with msg + memcpy(buf + 8, msg, len); + // calculate first hash + MD5((uint8_t *) buf, len + 64, (uint8_t * )(hash + 8)); // 16 bytes - // calculate digest - MD5((uint8_t *)hash, 96, digest); - } + // calculate digest + MD5((uint8_t *) hash, 96, digest); + } // AES #ifdef __AES__ - #define KeyExpansion256(round0,round1) \ - "pshufd $0xff, %%xmm2, %%xmm2 \n" \ - "movaps %%xmm1, %%xmm4 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm1 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm1 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm1 \n" \ - "pxor %%xmm2, %%xmm1 \n" \ - "movaps %%xmm1, "#round0"(%[sched]) \n" \ - "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ - "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ - "movaps %%xmm3, %%xmm4 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm3 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm3 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm3 \n" \ - "pxor %%xmm2, %%xmm3 \n" \ - "movaps %%xmm3, "#round1"(%[sched]) \n" +#define KeyExpansion256(round0,round1) \ + "pshufd $0xff, %%xmm2, %%xmm2 \n" \ + "movaps %%xmm1, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pxor %%xmm2, %%xmm1 \n" \ + "movaps %%xmm1, "#round0"(%[sched]) \n" \ + "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ + "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ + "movaps %%xmm3, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pxor %%xmm2, %%xmm3 \n" \ + "movaps %%xmm3, "#round1"(%[sched]) \n" #endif #ifdef __AES__ - void ECBCryptoAESNI::ExpandKey (const AESKey& key) - { - __asm__ - ( - "movups (%[key]), %%xmm1 \n" - "movups 16(%[key]), %%xmm3 \n" - "movaps %%xmm1, (%[sched]) \n" - "movaps %%xmm3, 16(%[sched]) \n" - "aeskeygenassist $1, %%xmm3, %%xmm2 \n" - KeyExpansion256(32,48) - "aeskeygenassist $2, %%xmm3, %%xmm2 \n" - KeyExpansion256(64,80) - "aeskeygenassist $4, %%xmm3, %%xmm2 \n" - KeyExpansion256(96,112) - "aeskeygenassist $8, %%xmm3, %%xmm2 \n" - KeyExpansion256(128,144) - "aeskeygenassist $16, %%xmm3, %%xmm2 \n" - KeyExpansion256(160,176) - "aeskeygenassist $32, %%xmm3, %%xmm2 \n" - KeyExpansion256(192,208) - "aeskeygenassist $64, %%xmm3, %%xmm2 \n" - // key expansion final - "pshufd $0xff, %%xmm2, %%xmm2 \n" - "movaps %%xmm1, %%xmm4 \n" - "pslldq $4, %%xmm4 \n" - "pxor %%xmm4, %%xmm1 \n" - "pslldq $4, %%xmm4 \n" - "pxor %%xmm4, %%xmm1 \n" - "pslldq $4, %%xmm4 \n" - "pxor %%xmm4, %%xmm1 \n" - "pxor %%xmm2, %%xmm1 \n" - "movups %%xmm1, 224(%[sched]) \n" - : // output - : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input - : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged - ); - } + void ECBCryptoAESNI::ExpandKey (const AESKey& key) + { + __asm__ + ( + "movups (%[key]), %%xmm1 \n" + "movups 16(%[key]), %%xmm3 \n" + "movaps %%xmm1, (%[sched]) \n" + "movaps %%xmm3, 16(%[sched]) \n" + "aeskeygenassist $1, %%xmm3, %%xmm2 \n" + KeyExpansion256(32,48) + "aeskeygenassist $2, %%xmm3, %%xmm2 \n" + KeyExpansion256(64,80) + "aeskeygenassist $4, %%xmm3, %%xmm2 \n" + KeyExpansion256(96,112) + "aeskeygenassist $8, %%xmm3, %%xmm2 \n" + KeyExpansion256(128,144) + "aeskeygenassist $16, %%xmm3, %%xmm2 \n" + KeyExpansion256(160,176) + "aeskeygenassist $32, %%xmm3, %%xmm2 \n" + KeyExpansion256(192,208) + "aeskeygenassist $64, %%xmm3, %%xmm2 \n" + // key expansion final + "pshufd $0xff, %%xmm2, %%xmm2 \n" + "movaps %%xmm1, %%xmm4 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pxor %%xmm2, %%xmm1 \n" + "movups %%xmm1, 224(%[sched]) \n" + : // output + : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input + : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged + ); + } #endif #ifdef __AES__ - #define EncryptAES256(sched) \ - "pxor (%["#sched"]), %%xmm0 \n" \ - "aesenc 16(%["#sched"]), %%xmm0 \n" \ - "aesenc 32(%["#sched"]), %%xmm0 \n" \ - "aesenc 48(%["#sched"]), %%xmm0 \n" \ - "aesenc 64(%["#sched"]), %%xmm0 \n" \ - "aesenc 80(%["#sched"]), %%xmm0 \n" \ - "aesenc 96(%["#sched"]), %%xmm0 \n" \ - "aesenc 112(%["#sched"]), %%xmm0 \n" \ - "aesenc 128(%["#sched"]), %%xmm0 \n" \ - "aesenc 144(%["#sched"]), %%xmm0 \n" \ - "aesenc 160(%["#sched"]), %%xmm0 \n" \ - "aesenc 176(%["#sched"]), %%xmm0 \n" \ - "aesenc 192(%["#sched"]), %%xmm0 \n" \ - "aesenc 208(%["#sched"]), %%xmm0 \n" \ - "aesenclast 224(%["#sched"]), %%xmm0 \n" +#define EncryptAES256(sched) \ + "pxor (%["#sched"]), %%xmm0 \n" \ + "aesenc 16(%["#sched"]), %%xmm0 \n" \ + "aesenc 32(%["#sched"]), %%xmm0 \n" \ + "aesenc 48(%["#sched"]), %%xmm0 \n" \ + "aesenc 64(%["#sched"]), %%xmm0 \n" \ + "aesenc 80(%["#sched"]), %%xmm0 \n" \ + "aesenc 96(%["#sched"]), %%xmm0 \n" \ + "aesenc 112(%["#sched"]), %%xmm0 \n" \ + "aesenc 128(%["#sched"]), %%xmm0 \n" \ + "aesenc 144(%["#sched"]), %%xmm0 \n" \ + "aesenc 160(%["#sched"]), %%xmm0 \n" \ + "aesenc 176(%["#sched"]), %%xmm0 \n" \ + "aesenc 192(%["#sched"]), %%xmm0 \n" \ + "aesenc 208(%["#sched"]), %%xmm0 \n" \ + "aesenclast 224(%["#sched"]), %%xmm0 \n" #endif - void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out) - { + void ECBEncryption::Encrypt(const ChipherBlock *in, ChipherBlock *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - "movups (%[in]), %%xmm0 \n" - EncryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" + ); + } + else #endif - { - AES_encrypt (in->buf, out->buf, &m_Key); - } - } + { + AES_encrypt(in->buf, out->buf, &m_Key); + } + } #ifdef __AES__ - #define DecryptAES256(sched) \ - "pxor 224(%["#sched"]), %%xmm0 \n" \ - "aesdec 208(%["#sched"]), %%xmm0 \n" \ - "aesdec 192(%["#sched"]), %%xmm0 \n" \ - "aesdec 176(%["#sched"]), %%xmm0 \n" \ - "aesdec 160(%["#sched"]), %%xmm0 \n" \ - "aesdec 144(%["#sched"]), %%xmm0 \n" \ - "aesdec 128(%["#sched"]), %%xmm0 \n" \ - "aesdec 112(%["#sched"]), %%xmm0 \n" \ - "aesdec 96(%["#sched"]), %%xmm0 \n" \ - "aesdec 80(%["#sched"]), %%xmm0 \n" \ - "aesdec 64(%["#sched"]), %%xmm0 \n" \ - "aesdec 48(%["#sched"]), %%xmm0 \n" \ - "aesdec 32(%["#sched"]), %%xmm0 \n" \ - "aesdec 16(%["#sched"]), %%xmm0 \n" \ - "aesdeclast (%["#sched"]), %%xmm0 \n" +#define DecryptAES256(sched) \ + "pxor 224(%["#sched"]), %%xmm0 \n" \ + "aesdec 208(%["#sched"]), %%xmm0 \n" \ + "aesdec 192(%["#sched"]), %%xmm0 \n" \ + "aesdec 176(%["#sched"]), %%xmm0 \n" \ + "aesdec 160(%["#sched"]), %%xmm0 \n" \ + "aesdec 144(%["#sched"]), %%xmm0 \n" \ + "aesdec 128(%["#sched"]), %%xmm0 \n" \ + "aesdec 112(%["#sched"]), %%xmm0 \n" \ + "aesdec 96(%["#sched"]), %%xmm0 \n" \ + "aesdec 80(%["#sched"]), %%xmm0 \n" \ + "aesdec 64(%["#sched"]), %%xmm0 \n" \ + "aesdec 48(%["#sched"]), %%xmm0 \n" \ + "aesdec 32(%["#sched"]), %%xmm0 \n" \ + "aesdec 16(%["#sched"]), %%xmm0 \n" \ + "aesdeclast (%["#sched"]), %%xmm0 \n" #endif - void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out) - { + void ECBDecryption::Decrypt(const ChipherBlock *in, ChipherBlock *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - "movups (%[in]), %%xmm0 \n" - DecryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" + ); + } + else #endif - { - AES_decrypt (in->buf, out->buf, &m_Key); - } - } + { + AES_decrypt(in->buf, out->buf, &m_Key); + } + } #ifdef __AES__ - #define CallAESIMC(offset) \ - "movaps "#offset"(%[shed]), %%xmm0 \n" \ - "aesimc %%xmm0, %%xmm0 \n" \ - "movaps %%xmm0, "#offset"(%[shed]) \n" +#define CallAESIMC(offset) \ + "movaps "#offset"(%[shed]), %%xmm0 \n" \ + "aesimc %%xmm0, %%xmm0 \n" \ + "movaps %%xmm0, "#offset"(%[shed]) \n" #endif - void ECBEncryption::SetKey (const AESKey& key) - { + void ECBEncryption::SetKey(const AESKey &key) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - ExpandKey (key); - } - else + if(i2p::cpu::aesni) + { + ExpandKey (key); + } + else #endif - { - AES_set_encrypt_key (key, 256, &m_Key); - } - } + { + AES_set_encrypt_key(key, 256, &m_Key); + } + } - void ECBDecryption::SetKey (const AESKey& key) - { + void ECBDecryption::SetKey(const AESKey &key) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - ExpandKey (key); // expand encryption key first - // then invert it using aesimc - __asm__ - ( - CallAESIMC(16) - CallAESIMC(32) - CallAESIMC(48) - CallAESIMC(64) - CallAESIMC(80) - CallAESIMC(96) - CallAESIMC(112) - CallAESIMC(128) - CallAESIMC(144) - CallAESIMC(160) - CallAESIMC(176) - CallAESIMC(192) - CallAESIMC(208) - : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + ExpandKey (key); // expand encryption key first + // then invert it using aesimc + __asm__ + ( + CallAESIMC(16) + CallAESIMC(32) + CallAESIMC(48) + CallAESIMC(64) + CallAESIMC(80) + CallAESIMC(96) + CallAESIMC(112) + CallAESIMC(128) + CallAESIMC(144) + CallAESIMC(160) + CallAESIMC(176) + CallAESIMC(192) + CallAESIMC(208) + : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" + ); + } + else #endif - { - AES_set_decrypt_key (key, 256, &m_Key); - } - } + { + AES_set_decrypt_key(key, 256, &m_Key); + } + } - void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) - { + void CBCEncryption::Encrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "1: \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched) - "movaps %%xmm0, %%xmm1 \n" - "movups %%xmm0, (%[out]) \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "dec %[num] \n" - "jnz 1b \n" - "movups %%xmm1, (%[iv]) \n" - : - : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) - : "%xmm0", "%xmm1", "cc", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "cc", "memory" + ); + } + else #endif - { - for (int i = 0; i < numBlocks; i++) - { - *m_LastBlock.GetChipherBlock () ^= in[i]; - m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ()); - out[i] = *m_LastBlock.GetChipherBlock (); - } - } - } + { + for (int i = 0; i < numBlocks; i++) { + *m_LastBlock.GetChipherBlock() ^= in[i]; + m_ECBEncryption.Encrypt(m_LastBlock.GetChipherBlock(), m_LastBlock.GetChipherBlock()); + out[i] = *m_LastBlock.GetChipherBlock(); + } + } + } - void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out) - { - // len/16 - int numBlocks = len >> 4; - if (numBlocks > 0) - Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); - } + void CBCEncryption::Encrypt(const uint8_t *in, std::size_t len, uint8_t *out) { + // len/16 + int numBlocks = len >> 4; + if (numBlocks > 0) + Encrypt(numBlocks, (const ChipherBlock *) in, (ChipherBlock *) out); + } - void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) - { + void CBCEncryption::Encrypt(const uint8_t *in, uint8_t *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - "movups %%xmm0, (%[iv]) \n" - : - : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out) - : "%xmm0", "%xmm1", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + "movups %%xmm0, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); + } + else #endif - Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); - } + Encrypt(1, (const ChipherBlock *) in, (ChipherBlock *) out); + } - void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) - { + void CBCDecryption::Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "1: \n" - "movups (%[in]), %%xmm0 \n" - "movaps %%xmm0, %%xmm2 \n" - DecryptAES256(sched) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - "movaps %%xmm2, %%xmm1 \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "dec %[num] \n" - "jnz 1b \n" - "movups %%xmm1, (%[iv]) \n" - : - : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) - : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); + } + else #endif - { - for (int i = 0; i < numBlocks; i++) - { - ChipherBlock tmp = in[i]; - m_ECBDecryption.Decrypt (in + i, out + i); - out[i] ^= *m_IV.GetChipherBlock (); - *m_IV.GetChipherBlock () = tmp; - } - } - } + { + for (int i = 0; i < numBlocks; i++) { + ChipherBlock tmp = in[i]; + m_ECBDecryption.Decrypt(in + i, out + i); + out[i] ^= *m_IV.GetChipherBlock(); + *m_IV.GetChipherBlock() = tmp; + } + } + } - void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out) - { - int numBlocks = len >> 4; - if (numBlocks > 0) - Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); - } + void CBCDecryption::Decrypt(const uint8_t *in, std::size_t len, uint8_t *out) { + int numBlocks = len >> 4; + if (numBlocks > 0) + Decrypt(numBlocks, (const ChipherBlock *) in, (ChipherBlock *) out); + } - void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) - { + void CBCDecryption::Decrypt(const uint8_t *in, uint8_t *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "movups (%[in]), %%xmm0 \n" - "movups %%xmm0, (%[iv]) \n" - DecryptAES256(sched) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - : - : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out) - : "%xmm0", "%xmm1", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "movups %%xmm0, (%[iv]) \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + : + : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); + } + else #endif - Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); - } + Decrypt(1, (const ChipherBlock *) in, (ChipherBlock *) out); + } - void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) - { + void TunnelEncryption::Encrypt(const uint8_t *in, uint8_t *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - // encrypt IV - "movups (%[in]), %%xmm0 \n" - EncryptAES256(sched_iv) - "movaps %%xmm0, %%xmm1 \n" - // double IV encryption - EncryptAES256(sched_iv) - "movups %%xmm0, (%[out]) \n" - // encrypt data, IV is xmm1 - "1: \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched_l) - "movaps %%xmm0, %%xmm1 \n" - "movups %%xmm0, (%[out]) \n" - "dec %[num] \n" - "jnz 1b \n" - : - : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes - : "%xmm0", "%xmm1", "cc", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + // encrypt IV + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + EncryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // encrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched_l) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "cc", "memory" + ); + } + else #endif - { - m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerEncryption.SetIV (out); - m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv - } - } + { + m_IVEncryption.Encrypt((const ChipherBlock *) in, (ChipherBlock *) out); // iv + m_LayerEncryption.SetIV(out); + m_LayerEncryption.Encrypt(in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data + m_IVEncryption.Encrypt((ChipherBlock *) out, (ChipherBlock *) out); // double iv + } + } - void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) - { + void TunnelDecryption::Decrypt(const uint8_t *in, uint8_t *out) { #ifdef __AES__ - if(i2p::cpu::aesni) - { - __asm__ - ( - // decrypt IV - "movups (%[in]), %%xmm0 \n" - DecryptAES256(sched_iv) - "movaps %%xmm0, %%xmm1 \n" - // double IV encryption - DecryptAES256(sched_iv) - "movups %%xmm0, (%[out]) \n" - // decrypt data, IV is xmm1 - "1: \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "movups (%[in]), %%xmm0 \n" - "movaps %%xmm0, %%xmm2 \n" - DecryptAES256(sched_l) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - "movaps %%xmm2, %%xmm1 \n" - "dec %[num] \n" - "jnz 1b \n" - : - : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes - : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" - ); - } - else + if(i2p::cpu::aesni) + { + __asm__ + ( + // decrypt IV + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + DecryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // decrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched_l) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); + } + else #endif - { - m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerDecryption.SetIV (out); - m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv - } - } + { + m_IVDecryption.Decrypt((const ChipherBlock *) in, (ChipherBlock *) out); // iv + m_LayerDecryption.SetIV(out); + m_LayerDecryption.Decrypt(in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data + m_IVDecryption.Decrypt((ChipherBlock *) out, (ChipherBlock *) out); // double iv + } + } // 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) - { - if (len < msgLen) return false; - if (encrypt && len < msgLen + 16) return false; - bool ret = true; + bool + AEADChaCha20Poly1305(const uint8_t *msg, size_t msgLen, const uint8_t *ad, size_t adLen, const uint8_t *key, + const uint8_t *nonce, uint8_t *buf, size_t len, bool encrypt) { + if (len < msgLen) return false; + if (encrypt && len < msgLen + 16) return false; + bool ret = true; #if OPENSSL_AEAD_CHACHA20_POLY1305 - int outlen = 0; - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); - if (encrypt) - { - EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); - EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); - EVP_EncryptUpdate(ctx, NULL, &outlen, ad, adLen); - EVP_EncryptUpdate(ctx, buf, &outlen, msg, msgLen); - EVP_EncryptFinal_ex(ctx, buf, &outlen); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, buf + msgLen); - } - else - { - EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen)); - EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce); - EVP_DecryptUpdate(ctx, NULL, &outlen, ad, adLen); - EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen); - ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0; - } + int outlen = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); + if (encrypt) + { + EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); + EVP_EncryptUpdate(ctx, NULL, &outlen, ad, adLen); + EVP_EncryptUpdate(ctx, buf, &outlen, msg, msgLen); + EVP_EncryptFinal_ex(ctx, buf, &outlen); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, buf + msgLen); + } + else + { + EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen)); + EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce); + EVP_DecryptUpdate(ctx, NULL, &outlen, ad, adLen); + EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen); + ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0; + } - EVP_CIPHER_CTX_free (ctx); + EVP_CIPHER_CTX_free (ctx); #else - chacha::Chacha20State state; - // generate one time poly key - chacha::Chacha20Init (state, nonce, key, 0); - uint64_t polyKey[8]; - memset(polyKey, 0, sizeof(polyKey)); - chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64); - // create Poly1305 hash - Poly1305 polyHash (polyKey); - if (!ad) adLen = 0; - uint8_t padding[16]; memset (padding, 0, 16); - if (ad) - { - polyHash.Update (ad, adLen);// additional authenticated data - auto rem = adLen & 0x0F; // %16 - if (rem) - { - // padding1 - rem = 16 - rem; - polyHash.Update (padding, rem); - } - } - // encrypt/decrypt data and add to hash - Chacha20SetCounter (state, 1); - if (buf != msg) - memcpy (buf, msg, msgLen); - if (encrypt) - { - chacha::Chacha20Encrypt (state, buf, msgLen); // encrypt - polyHash.Update (buf, msgLen); // after encryption - } - else - { - polyHash.Update (buf, msgLen); // before decryption - chacha::Chacha20Encrypt (state, buf, msgLen); // decrypt - } + chacha::Chacha20State state; + // generate one time poly key + chacha::Chacha20Init(state, nonce, key, 0); + uint64_t polyKey[8]; + memset(polyKey, 0, sizeof(polyKey)); + chacha::Chacha20Encrypt(state, (uint8_t *) polyKey, 64); + // create Poly1305 hash + Poly1305 polyHash(polyKey); + if (!ad) adLen = 0; + uint8_t padding[16]; + memset(padding, 0, 16); + if (ad) { + polyHash.Update(ad, adLen);// additional authenticated data + auto rem = adLen & 0x0F; // %16 + if (rem) { + // padding1 + rem = 16 - rem; + polyHash.Update(padding, rem); + } + } + // encrypt/decrypt data and add to hash + Chacha20SetCounter(state, 1); + if (buf != msg) + memcpy(buf, msg, msgLen); + if (encrypt) { + chacha::Chacha20Encrypt(state, buf, msgLen); // encrypt + polyHash.Update(buf, msgLen); // after encryption + } else { + polyHash.Update(buf, msgLen); // before decryption + chacha::Chacha20Encrypt(state, buf, msgLen); // decrypt + } - auto rem = msgLen & 0x0F; // %16 - if (rem) - { - // padding2 - rem = 16 - rem; - polyHash.Update (padding, rem); - } - // adLen and msgLen - htole64buf (padding, adLen); - htole64buf (padding + 8, msgLen); - polyHash.Update (padding, 16); + auto rem = msgLen & 0x0F; // %16 + if (rem) { + // padding2 + rem = 16 - rem; + polyHash.Update(padding, rem); + } + // adLen and msgLen + htole64buf(padding, adLen); + htole64buf(padding + 8, msgLen); + polyHash.Update(padding, 16); - if (encrypt) - // calculate Poly1305 tag and write in after encrypted data - polyHash.Finish ((uint64_t *)(buf + msgLen)); - else - { - uint64_t tag[4]; - // calculate Poly1305 tag - polyHash.Finish (tag); - if (memcmp (tag, msg + msgLen, 16)) ret = false; // compare with provided - } + if (encrypt) + // calculate Poly1305 tag and write in after encrypted data + polyHash.Finish((uint64_t * )(buf + msgLen)); + else { + uint64_t tag[4]; + // calculate Poly1305 tag + polyHash.Finish(tag); + if (memcmp(tag, msg + msgLen, 16)) ret = false; // compare with provided + } #endif - return ret; - } + return ret; + } - void AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) - { - if (bufs.empty ()) return; + void AEADChaCha20Poly1305Encrypt(const std::vector > &bufs, const uint8_t *key, + const uint8_t *nonce, uint8_t *mac) { + if (bufs.empty()) return; #if OPENSSL_AEAD_CHACHA20_POLY1305 - int outlen = 0; - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); - EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); - EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); - for (const auto& it: bufs) - EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second); - EVP_EncryptFinal_ex(ctx, NULL, &outlen); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); - EVP_CIPHER_CTX_free (ctx); + int outlen = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); + EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); + for (const auto& it: bufs) + EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second); + EVP_EncryptFinal_ex(ctx, NULL, &outlen); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); + EVP_CIPHER_CTX_free (ctx); #else - chacha::Chacha20State state; - // generate one time poly key - chacha::Chacha20Init (state, nonce, key, 0); - uint64_t polyKey[8]; - memset(polyKey, 0, sizeof(polyKey)); - chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64); - Poly1305 polyHash (polyKey); - // encrypt buffers - Chacha20SetCounter (state, 1); - size_t size = 0; - for (const auto& it: bufs) - { - chacha::Chacha20Encrypt (state, it.first, it.second); - polyHash.Update (it.first, it.second); // after encryption - size += it.second; - } - // padding - uint8_t padding[16]; - memset (padding, 0, 16); - auto rem = size & 0x0F; // %16 - if (rem) - { - // padding2 - rem = 16 - rem; - polyHash.Update (padding, rem); - } - // adLen and msgLen - // adLen is always zero - htole64buf (padding + 8, size); - polyHash.Update (padding, 16); - // MAC - polyHash.Finish ((uint64_t *)mac); + chacha::Chacha20State state; + // generate one time poly key + chacha::Chacha20Init(state, nonce, key, 0); + uint64_t polyKey[8]; + memset(polyKey, 0, sizeof(polyKey)); + chacha::Chacha20Encrypt(state, (uint8_t *) polyKey, 64); + Poly1305 polyHash(polyKey); + // encrypt buffers + Chacha20SetCounter(state, 1); + size_t size = 0; + for (const auto &it: bufs) { + chacha::Chacha20Encrypt(state, it.first, it.second); + polyHash.Update(it.first, it.second); // after encryption + size += it.second; + } + // padding + uint8_t padding[16]; + memset(padding, 0, 16); + auto rem = size & 0x0F; // %16 + if (rem) { + // padding2 + rem = 16 - rem; + polyHash.Update(padding, rem); + } + // adLen and msgLen + // adLen is always zero + htole64buf(padding + 8, size); + polyHash.Update(padding, 16); + // MAC + polyHash.Finish((uint64_t *) mac); #endif - } + } - void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) - { + void ChaCha20(const uint8_t *msg, size_t msgLen, const uint8_t *key, const uint8_t *nonce, uint8_t *out) { #if OPENSSL_AEAD_CHACHA20_POLY1305 - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); - uint32_t iv[4]; - iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce - EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv); - int outlen = 0; - EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen); - EVP_EncryptFinal_ex(ctx, NULL, &outlen); - EVP_CIPHER_CTX_free (ctx); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); + uint32_t iv[4]; + iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce + EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv); + int outlen = 0; + EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen); + EVP_EncryptFinal_ex(ctx, NULL, &outlen); + EVP_CIPHER_CTX_free (ctx); #else - chacha::Chacha20State state; - chacha::Chacha20Init (state, nonce, key, 1); - if (out != msg) memcpy (out, msg, msgLen); - chacha::Chacha20Encrypt (state, out, msgLen); + chacha::Chacha20State state; + chacha::Chacha20Init(state, nonce, key, 1); + if (out != msg) memcpy(out, msg, msgLen); + chacha::Chacha20Encrypt(state, out, msgLen); #endif - } + } - void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, - uint8_t * out, size_t outLen) - { + void HKDF(const uint8_t *salt, const uint8_t *key, size_t keyLen, const std::string &info, + uint8_t *out, size_t outLen) { #if OPENSSL_HKDF - EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr); - EVP_PKEY_derive_init (pctx); - EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256()); - if (key && keyLen) - { - EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); - EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); - } - else - { - // zerolen - EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); - uint8_t tempKey[32]; unsigned int len; - HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len); - EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); - } - if (info.length () > 0) - EVP_PKEY_CTX_add1_hkdf_info (pctx, (const uint8_t *)info.c_str (), info.length ()); - EVP_PKEY_derive (pctx, out, &outLen); - EVP_PKEY_CTX_free (pctx); + EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr); + EVP_PKEY_derive_init (pctx); + EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256()); + if (key && keyLen) + { + EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); + EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); + } + else + { + // zerolen + EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); + uint8_t tempKey[32]; unsigned int len; + HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len); + EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); + } + if (info.length () > 0) + EVP_PKEY_CTX_add1_hkdf_info (pctx, (const uint8_t *)info.c_str (), info.length ()); + EVP_PKEY_derive (pctx, out, &outLen); + EVP_PKEY_CTX_free (pctx); #else - uint8_t prk[32]; unsigned int len; - HMAC(EVP_sha256(), salt, 32, key, keyLen, prk, &len); - auto l = info.length (); - memcpy (out, info.c_str (), l); out[l] = 0x01; - HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len); - if (outLen > 32) // 64 - { - memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02; - HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); - } + uint8_t prk[32]; + unsigned int len; + HMAC(EVP_sha256(), salt, 32, key, keyLen, prk, &len); + auto l = info.length(); + memcpy(out, info.c_str(), l); + out[l] = 0x01; + HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len); + if (outLen > 32) // 64 + { + memcpy(out + 32, info.c_str(), l); + out[l + 32] = 0x02; + HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); + } #endif - } + } // Noise - void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len) - { - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, m_H, 32); - SHA256_Update (&ctx, buf, len); - SHA256_Final (m_H, &ctx); - } + void NoiseSymmetricState::MixHash(const uint8_t *buf, size_t len) { + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, m_H, 32); + SHA256_Update(&ctx, buf, len); + SHA256_Final(m_H, &ctx); + } - void NoiseSymmetricState::MixHash (const std::vector >& bufs) - { - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, m_H, 32); - for (const auto& it: bufs) - SHA256_Update (&ctx, it.first, it.second); - SHA256_Final (m_H, &ctx); - } + void NoiseSymmetricState::MixHash(const std::vector > &bufs) { + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, m_H, 32); + for (const auto &it: bufs) + SHA256_Update(&ctx, it.first, it.second); + SHA256_Final(m_H, &ctx); + } - void NoiseSymmetricState::MixKey (const uint8_t * sharedSecret) - { - HKDF (m_CK, sharedSecret, 32, "", m_CK); - // new ck is m_CK[0:31], key is m_CK[32:63] - } + void NoiseSymmetricState::MixKey(const uint8_t *sharedSecret) { + HKDF(m_CK, sharedSecret, 32, "", m_CK); + // new ck is m_CK[0:31], key is m_CK[32:63] + } - static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck, - const uint8_t * hh, const uint8_t * pub) - { - // pub is Bob's public static key, hh = SHA256(h) - memcpy (state.m_CK, ck, 32); - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, hh, 32); - SHA256_Update (&ctx, pub, 32); - SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub) - } + static void InitNoiseState(NoiseSymmetricState &state, const uint8_t *ck, + const uint8_t *hh, const uint8_t *pub) { + // pub is Bob's public static key, hh = SHA256(h) + memcpy(state.m_CK, ck, 32); + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, hh, 32); + SHA256_Update(&ctx, pub, 32); + SHA256_Final(state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub) + } - void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub) - { - static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars - static const uint8_t hh[32] = - { - 0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1, - 0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54 - }; // hh = SHA256(protocol_name || 0) - InitNoiseState (state, (const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0 - } + void InitNoiseNState(NoiseSymmetricState &state, const uint8_t *pub) { + static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars + static const uint8_t hh[32] = + { + 0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, + 0xc1, + 0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, + 0x54 + }; // hh = SHA256(protocol_name || 0) + InitNoiseState(state, (const uint8_t *) protocolName, hh, pub); // ck = protocol_name || 0 + } - void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub) - { - static const uint8_t protocolNameHash[32] = - { - 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, - 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 - }; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256") - static const uint8_t hh[32] = - { - 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, - 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e - }; // SHA256 (protocolNameHash) - InitNoiseState (state, protocolNameHash, hh, pub); - } + void InitNoiseXKState(NoiseSymmetricState &state, const uint8_t *pub) { + static const uint8_t protocolNameHash[32] = + { + 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, + 0xed, + 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, + 0x71 + }; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256") + static const uint8_t hh[32] = + { + 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, + 0xb5, + 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, + 0x1e + }; // SHA256 (protocolNameHash) + InitNoiseState(state, protocolNameHash, hh, pub); + } - void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub) - { - static const uint8_t protocolNameHash[32] = - { - 0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4, - 0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32 - }; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256") - static const uint8_t hh[32] = - { - 0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53, - 0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33 - }; // SHA256 (protocolNameHash) - InitNoiseState (state, protocolNameHash, hh, pub); - } + void InitNoiseXKState1(NoiseSymmetricState &state, const uint8_t *pub) { + static const uint8_t protocolNameHash[32] = + { + 0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, + 0xf4, + 0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, + 0x32 + }; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256") + static const uint8_t hh[32] = + { + 0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, + 0x53, + 0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, + 0x33 + }; // SHA256 (protocolNameHash) + InitNoiseState(state, protocolNameHash, hh, pub); + } - void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub) - { - static const uint8_t protocolNameHash[32] = - { - 0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba, - 0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c - }; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes - static const uint8_t hh[32] = - { - 0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32, - 0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c - }; // SHA256 (protocolNameHash) - InitNoiseState (state, protocolNameHash, hh, pub); - } + void InitNoiseIKState(NoiseSymmetricState &state, const uint8_t *pub) { + static const uint8_t protocolNameHash[32] = + { + 0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, + 0xba, + 0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, + 0x0c + }; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes + static const uint8_t hh[32] = + { + 0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, + 0x32, + 0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, + 0x5c + }; // SHA256 (protocolNameHash) + InitNoiseState(state, protocolNameHash, hh, pub); + } // init and terminate @@ -1403,43 +1360,40 @@ namespace crypto } }*/ - void InitCrypto (bool precomputation, bool aesni, bool avx, bool force) - { - i2p::cpu::Detect (aesni, avx, force); + void InitCrypto(bool precomputation, bool aesni, bool avx, bool force) { + i2p::cpu::Detect(aesni, avx, force); #if LEGACY_OPENSSL - SSL_library_init (); + SSL_library_init(); #endif /* auto numLocks = CRYPTO_num_locks(); for (int i = 0; i < numLocks; i++) m_OpenSSLMutexes.emplace_back (new std::mutex); CRYPTO_set_locking_callback (OpensslLockingCallback);*/ - if (precomputation) - { + if (precomputation) { #if defined(__x86_64__) - g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255]; - PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES); + g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255]; + PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES); #else - g_ElggTable = new BIGNUM * [ELGAMAL_SHORT_EXPONENT_NUM_BYTES][255]; - PrecalculateElggTable (g_ElggTable, ELGAMAL_SHORT_EXPONENT_NUM_BYTES); + g_ElggTable = new BIGNUM *[ELGAMAL_SHORT_EXPONENT_NUM_BYTES][255]; + PrecalculateElggTable(g_ElggTable, ELGAMAL_SHORT_EXPONENT_NUM_BYTES); #endif - } - } + } + } - void TerminateCrypto () - { - if (g_ElggTable) - { - DestroyElggTable (g_ElggTable, + void TerminateCrypto() { + if (g_ElggTable) { + DestroyElggTable(g_ElggTable, #if defined(__x86_64__) - ELGAMAL_FULL_EXPONENT_NUM_BYTES + ELGAMAL_FULL_EXPONENT_NUM_BYTES #else - ELGAMAL_SHORT_EXPONENT_NUM_BYTES + ELGAMAL_SHORT_EXPONENT_NUM_BYTES #endif - ); - delete[] g_ElggTable; g_ElggTable = nullptr; - } + ); + delete[] g_ElggTable; + g_ElggTable = nullptr; + } /* CRYPTO_set_locking_callback (nullptr); m_OpenSSLMutexes.clear ();*/ - } -} + } + } } diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index c6dcd2cc..ad9b5175 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -50,359 +50,406 @@ # endif #endif -namespace i2p -{ -namespace crypto -{ - bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len); +namespace i2p { + namespace crypto { + bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len); - // DSA - DSA * CreateDSA (); + // DSA + DSA *CreateDSA(); - // RSA - const BIGNUM * GetRSAE (); + // RSA + const BIGNUM *GetRSAE(); - // DH - class DHKeys - { - public: + // DH + class DHKeys { + public: - DHKeys (); - ~DHKeys (); + DHKeys(); - void GenerateKeys (); - const uint8_t * GetPublicKey () const { return m_PublicKey; }; - void Agree (const uint8_t * pub, uint8_t * shared); + ~DHKeys(); - private: + void GenerateKeys(); - DH * m_DH; - uint8_t m_PublicKey[256]; - }; + const uint8_t *GetPublicKey() const { return m_PublicKey; }; - // x25519 - class X25519Keys - { - public: + void Agree(const uint8_t *pub, uint8_t *shared); - X25519Keys (); - X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv - ~X25519Keys (); + private: - 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); + DH *m_DH; + uint8_t m_PublicKey[256]; + }; - bool IsElligatorIneligible () const { return m_IsElligatorIneligible; } - void SetElligatorIneligible () { m_IsElligatorIneligible = true; } + // x25519 + 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 - EVP_PKEY_CTX * m_Ctx; - EVP_PKEY * m_Pkey; + EVP_PKEY_CTX * m_Ctx; + EVP_PKEY * m_Pkey; #else - BN_CTX * m_Ctx; - uint8_t m_PrivateKey[32]; + BN_CTX *m_Ctx; + uint8_t m_PrivateKey[32]; #endif - bool m_IsElligatorIneligible = false; // true if definitely ineligible - }; + bool m_IsElligatorIneligible = false; // true if definitely ineligible + }; - // ElGamal - void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted - 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); + // ElGamal + void ElGamalEncrypt(const uint8_t *key, const uint8_t *data, + uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted + 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 - void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted - 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); + // ECIES + void ECIESEncrypt(const EC_GROUP *curve, const EC_POINT *key, const uint8_t *data, + uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted + 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 - typedef i2p::data::Tag<32> MACKey; - void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest); + // HMAC + typedef i2p::data::Tag<32> MACKey; - // AES - struct ChipherBlock - { - uint8_t buf[16]; + void HMACMD5Digest(uint8_t *msg, size_t len, const MACKey &key, uint8_t *digest); - void operator^=(const ChipherBlock& other) // XOR - { - if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ? - { - for (int i = 0; i < 4; i++) - reinterpret_cast(buf)[i] ^= reinterpret_cast(other.buf)[i]; - } - else - { - for (int i = 0; i < 16; i++) - buf[i] ^= other.buf[i]; - } - } - }; + // AES + struct ChipherBlock { + uint8_t buf[16]; - 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(buf)[i] ^= reinterpret_cast(other.buf)[i]; + } else { + for (int i = 0; i < 16; i++) + buf[i] ^= other.buf[i]; + } + } + }; - template - class AESAlignedBuffer // 16 bytes alignment - { - public: + typedef i2p::data::Tag<32> AESKey; - AESAlignedBuffer () - { - m_Buf = m_UnalignedBuffer; - uint8_t rem = ((size_t)m_Buf) & 0x0f; - if (rem) - m_Buf += (16 - rem); - } + template + class AESAlignedBuffer // 16 bytes alignment + { + public: - operator uint8_t * () { return m_Buf; }; - operator const uint8_t * () const { return m_Buf; }; - ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; }; - const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; }; + AESAlignedBuffer() { + m_Buf = m_UnalignedBuffer; + uint8_t rem = ((size_t) m_Buf) & 0x0f; + if (rem) + m_Buf += (16 - rem); + } - private: + operator uint8_t *() { return m_Buf; }; - uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment - uint8_t * m_Buf; - }; + operator const uint8_t *() const { return 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__ - class ECBCryptoAESNI - { - public: + class ECBCryptoAESNI + { + 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 #ifdef __AES__ - class ECBEncryption: public ECBCryptoAESNI + class ECBEncryption: public ECBCryptoAESNI #else - class ECBEncryption + + class ECBEncryption #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: - AES_KEY m_Key; - }; + private: + AES_KEY m_Key; + }; #ifdef __AES__ - class ECBDecryption: public ECBCryptoAESNI + class ECBDecryption: public ECBCryptoAESNI #else - class ECBDecryption + + class ECBDecryption #endif - { - public: + { + public: - void SetKey (const AESKey& key); - void Decrypt (const ChipherBlock * in, ChipherBlock * out); - private: - AES_KEY m_Key; - }; + void SetKey(const AESKey &key); - class CBCEncryption - { - public: + void Decrypt(const ChipherBlock *in, ChipherBlock *out); - CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); }; + private: + AES_KEY m_Key; + }; - 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); }; + class CBCEncryption { + public: - void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); - void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); - void Encrypt (const uint8_t * in, uint8_t * out); // one block + CBCEncryption() { memset((uint8_t *) m_LastBlock, 0, 16); }; - 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 - { - public: + ECBEncryption &ECB() { return m_ECBEncryption; } - CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); }; + private: - void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes - 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); }; + AESAlignedBuffer<16> m_LastBlock; - void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); - void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); - void Decrypt (const uint8_t * in, uint8_t * out); // one block + ECBEncryption m_ECBEncryption; + }; - ECBDecryption & ECB() { return m_ECBDecryption; } + class CBCDecryption { + public: - private: + CBCDecryption() { memset((uint8_t *) m_IV, 0, 16); }; - AESAlignedBuffer<16> m_IV; - ECBDecryption m_ECBDecryption; - }; + void SetKey(const AESKey &key) { m_ECBDecryption.SetKey(key); }; // 32 bytes + 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 - { - public: + void Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out); - void SetKeys (const AESKey& layerKey, const AESKey& ivKey) - { - m_LayerEncryption.SetKey (layerKey); - m_IVEncryption.SetKey (ivKey); - } + void Decrypt(const uint8_t *in, std::size_t len, uint8_t *out); - 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; - CBCEncryption m_LayerEncryption; - }; + private: - class TunnelDecryption // with double IV encryption - { - public: + AESAlignedBuffer<16> m_IV; + ECBDecryption m_ECBDecryption; + }; - void SetKeys (const AESKey& layerKey, const AESKey& ivKey) - { - m_LayerDecryption.SetKey (layerKey); - m_IVDecryption.SetKey (ivKey); - } + class TunnelEncryption // with double IV encryption + { + public: - 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; - CBCDecryption m_LayerDecryption; - }; + private: + + 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 - 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 >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad + void AEADChaCha20Poly1305Encrypt(const std::vector > &bufs, const uint8_t *key, + const uint8_t *nonce, uint8_t *mac); // encrypt multiple buffers with zero ad // 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 - 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 - struct NoiseSymmetricState - { - uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/; + struct NoiseSymmetricState { + uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/; - void MixHash (const uint8_t * buf, size_t len); - void MixHash (const std::vector >& bufs); - void MixKey (const uint8_t * sharedSecret); - }; + void MixHash(const uint8_t *buf, size_t len); - 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) + void MixHash(const std::vector > &bufs); + + void MixKey(const uint8_t *sharedSecret); + }; + + 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 - void InitCrypto (bool precomputation, bool aesni, bool avx, bool force); - void TerminateCrypto (); -} + void InitCrypto(bool precomputation, bool aesni, bool avx, bool force); + + void TerminateCrypto(); + } } // take care about openssl below 1.1.0 #if LEGACY_OPENSSL + // define getters and setters introduced in 1.1.0 -inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) - { - if (d->p) BN_free (d->p); - if (d->q) BN_free (d->q); - if (d->g) BN_free (d->g); - d->p = p; d->q = q; d->g = g; return 1; - } -inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) - { - 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 DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) { + if (d->p) BN_free(d->p); + if (d->q) BN_free(d->q); + if (d->g) BN_free(d->g); + d->p = p; + d->q = q; + d->g = g; + return 1; +} -inline int ECDSA_SIG_set0(ECDSA_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 ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) - { *pr = sig->r; *ps = sig->s; } +inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) { + 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 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 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 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 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 RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) - { return pkey->pkey.rsa; } +inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) { + *pr = sig->r; + *ps = sig->s; +} -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); } +inline int ECDSA_SIG_set0(ECDSA_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 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 #define TLS_method TLSv1_method diff --git a/libi2pd/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index ad986129..adf599f8 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -11,175 +11,154 @@ #include "Gost.h" #include "CryptoKey.h" -namespace i2p -{ -namespace crypto -{ - ElGamalEncryptor::ElGamalEncryptor (const uint8_t * pub) - { - memcpy (m_PublicKey, pub, 256); - } +namespace i2p { + namespace crypto { + ElGamalEncryptor::ElGamalEncryptor(const uint8_t *pub) { + memcpy(m_PublicKey, pub, 256); + } - void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) - { - ElGamalEncrypt (m_PublicKey, data, encrypted); - } + void ElGamalEncryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) { + ElGamalEncrypt(m_PublicKey, data, encrypted); + } - ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv) - { - memcpy (m_PrivateKey, priv, 256); - } + ElGamalDecryptor::ElGamalDecryptor(const uint8_t *priv) { + memcpy(m_PrivateKey, priv, 256); + } - bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) - { - return ElGamalDecrypt (m_PrivateKey, encrypted, data); - } + bool ElGamalDecryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) { + return ElGamalDecrypt(m_PrivateKey, encrypted, data); + } - ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub) - { - m_Curve = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); - m_PublicKey = EC_POINT_new (m_Curve); - BIGNUM * x = BN_bin2bn (pub, 32, nullptr); - BIGNUM * y = BN_bin2bn (pub + 32, 32, nullptr); - if (!EC_POINT_set_affine_coordinates_GFp (m_Curve, m_PublicKey, x, y, nullptr)) - LogPrint (eLogError, "ECICS P256 invalid public key"); - BN_free (x); BN_free (y); - } + ECIESP256Encryptor::ECIESP256Encryptor(const uint8_t *pub) { + m_Curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + m_PublicKey = EC_POINT_new(m_Curve); + BIGNUM *x = BN_bin2bn(pub, 32, nullptr); + BIGNUM *y = BN_bin2bn(pub + 32, 32, nullptr); + if (!EC_POINT_set_affine_coordinates_GFp(m_Curve, m_PublicKey, x, y, nullptr)) + LogPrint(eLogError, "ECICS P256 invalid public key"); + BN_free(x); + BN_free(y); + } - ECIESP256Encryptor::~ECIESP256Encryptor () - { - if (m_Curve) EC_GROUP_free (m_Curve); - if (m_PublicKey) EC_POINT_free (m_PublicKey); - } + ECIESP256Encryptor::~ECIESP256Encryptor() { + if (m_Curve) EC_GROUP_free(m_Curve); + if (m_PublicKey) EC_POINT_free(m_PublicKey); + } - void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) - { - if (m_Curve && m_PublicKey) - ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted); - } + void ECIESP256Encryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) { + if (m_Curve && m_PublicKey) + ECIESEncrypt(m_Curve, m_PublicKey, data, encrypted); + } - ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv) - { - m_Curve = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); - m_PrivateKey = BN_bin2bn (priv, 32, nullptr); - } + ECIESP256Decryptor::ECIESP256Decryptor(const uint8_t *priv) { + m_Curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + m_PrivateKey = BN_bin2bn(priv, 32, nullptr); + } - ECIESP256Decryptor::~ECIESP256Decryptor () - { - if (m_Curve) EC_GROUP_free (m_Curve); - if (m_PrivateKey) BN_free (m_PrivateKey); - } + ECIESP256Decryptor::~ECIESP256Decryptor() { + if (m_Curve) EC_GROUP_free(m_Curve); + if (m_PrivateKey) BN_free(m_PrivateKey); + } - bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) - { - if (m_Curve && m_PrivateKey) - return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data); - return false; - } + bool ECIESP256Decryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) { + if (m_Curve && m_PrivateKey) + return ECIESDecrypt(m_Curve, m_PrivateKey, encrypted, data); + return false; + } - void CreateECIESP256RandomKeys (uint8_t * priv, uint8_t * pub) - { - EC_GROUP * curve = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); - EC_POINT * p = nullptr; - BIGNUM * key = nullptr; - GenerateECIESKeyPair (curve, key, p); - bn2buf (key, priv, 32); - RAND_bytes (priv + 32, 224); - BN_free (key); - BIGNUM * x = BN_new (), * y = BN_new (); - EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, NULL); - bn2buf (x, pub, 32); - bn2buf (y, pub + 32, 32); - RAND_bytes (pub + 64, 192); - EC_POINT_free (p); - BN_free (x); BN_free (y); - EC_GROUP_free (curve); - } + void CreateECIESP256RandomKeys(uint8_t *priv, uint8_t *pub) { + EC_GROUP *curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + EC_POINT *p = nullptr; + BIGNUM *key = nullptr; + GenerateECIESKeyPair(curve, key, p); + bn2buf(key, priv, 32); + RAND_bytes(priv + 32, 224); + BN_free(key); + BIGNUM *x = BN_new(), *y = BN_new(); + EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, NULL); + bn2buf(x, pub, 32); + bn2buf(y, pub + 32, 32); + RAND_bytes(pub + 64, 192); + EC_POINT_free(p); + BN_free(x); + BN_free(y); + EC_GROUP_free(curve); + } - ECIESGOSTR3410Encryptor::ECIESGOSTR3410Encryptor (const uint8_t * pub) - { - auto& curve = GetGOSTR3410Curve (eGOSTR3410CryptoProA); - m_PublicKey = EC_POINT_new (curve->GetGroup ()); - BIGNUM * x = BN_bin2bn (pub, 32, nullptr); - BIGNUM * y = BN_bin2bn (pub + 32, 32, 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"); - BN_free (x); BN_free (y); - } + ECIESGOSTR3410Encryptor::ECIESGOSTR3410Encryptor(const uint8_t *pub) { + auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA); + m_PublicKey = EC_POINT_new(curve->GetGroup()); + BIGNUM *x = BN_bin2bn(pub, 32, nullptr); + BIGNUM *y = BN_bin2bn(pub + 32, 32, 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"); + BN_free(x); + BN_free(y); + } - ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor () - { - if (m_PublicKey) EC_POINT_free (m_PublicKey); - } + ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor() { + if (m_PublicKey) EC_POINT_free(m_PublicKey); + } - void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) - { - if (m_PublicKey) - ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted); - } + void ECIESGOSTR3410Encryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) { + if (m_PublicKey) + ECIESEncrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PublicKey, data, encrypted); + } - ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv) - { - m_PrivateKey = BN_bin2bn (priv, 32, nullptr); - } + ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor(const uint8_t *priv) { + m_PrivateKey = BN_bin2bn(priv, 32, nullptr); + } - ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor () - { - if (m_PrivateKey) BN_free (m_PrivateKey); - } + ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor() { + if (m_PrivateKey) BN_free(m_PrivateKey); + } - bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) - { - if (m_PrivateKey) - return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data); - return false; - } + bool ECIESGOSTR3410Decryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) { + if (m_PrivateKey) + return ECIESDecrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PrivateKey, encrypted, data); + return false; + } - void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub) - { - auto& curve = GetGOSTR3410Curve (eGOSTR3410CryptoProA); - EC_POINT * p = nullptr; - BIGNUM * key = nullptr; - GenerateECIESKeyPair (curve->GetGroup (), key, p); - bn2buf (key, priv, 32); - RAND_bytes (priv + 32, 224); - BN_free (key); - BIGNUM * x = BN_new (), * y = BN_new (); - EC_POINT_get_affine_coordinates_GFp (curve->GetGroup (), p, x, y, NULL); - bn2buf (x, pub, 32); - bn2buf (y, pub + 32, 32); - RAND_bytes (pub + 64, 192); - EC_POINT_free (p); - BN_free (x); BN_free (y); - } + void CreateECIESGOSTR3410RandomKeys(uint8_t *priv, uint8_t *pub) { + auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA); + EC_POINT *p = nullptr; + BIGNUM *key = nullptr; + GenerateECIESKeyPair(curve->GetGroup(), key, p); + bn2buf(key, priv, 32); + RAND_bytes(priv + 32, 224); + BN_free(key); + BIGNUM *x = BN_new(), *y = BN_new(); + EC_POINT_get_affine_coordinates_GFp(curve->GetGroup(), p, x, y, NULL); + bn2buf(x, pub, 32); + bn2buf(y, pub + 32, 32); + RAND_bytes(pub + 64, 192); + EC_POINT_free(p); + BN_free(x); + BN_free(y); + } - ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub) - { - memcpy (m_PublicKey, pub, 32); - } + ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor(const uint8_t *pub) { + memcpy(m_PublicKey, pub, 32); + } - void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub) - { - memcpy (pub, m_PublicKey, 32); - } + void ECIESX25519AEADRatchetEncryptor::Encrypt(const uint8_t *, uint8_t *pub) { + memcpy(pub, m_PublicKey, 32); + } - ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic) - { - m_StaticKeys.SetPrivateKey (priv, calculatePublic); - } + ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic) { + m_StaticKeys.SetPrivateKey(priv, calculatePublic); + } - bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret) - { - return m_StaticKeys.Agree (epub, sharedSecret); - } + bool ECIESX25519AEADRatchetDecryptor::Decrypt(const uint8_t *epub, uint8_t *sharedSecret) { + return m_StaticKeys.Agree(epub, sharedSecret); + } - void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub) - { - X25519Keys k; - k.GenerateKeys (); - k.GetPrivateKey (priv); - memcpy (pub, k.GetPublicKey (), 32); - } -} + void CreateECIESX25519AEADRatchetRandomKeys(uint8_t *priv, uint8_t *pub) { + X25519Keys k; + k.GenerateKeys(); + k.GetPrivateKey(priv); + memcpy(pub, k.GetPublicKey(), 32); + } + } } diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index a7d86d09..cb96b79f 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -12,153 +12,164 @@ #include #include "Crypto.h" -namespace i2p -{ -namespace crypto -{ - class CryptoKeyEncryptor - { - public: +namespace i2p { + namespace crypto { + class CryptoKeyEncryptor { + public: - virtual ~CryptoKeyEncryptor () {}; - virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) = 0; - }; + virtual ~CryptoKeyEncryptor() {}; - class CryptoKeyDecryptor - { - public: + virtual void Encrypt(const uint8_t *data, uint8_t *encrypted) = 0; + }; - 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 - }; + class CryptoKeyDecryptor { + public: + + 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 - class ElGamalEncryptor: public CryptoKeyEncryptor // for destination - { - public: + class ElGamalEncryptor : public CryptoKeyEncryptor // for destination + { + public: - ElGamalEncryptor (const uint8_t * pub); - void Encrypt (const uint8_t * data, uint8_t * encrypted) override; // 222 bytes data, 514 bytes encrypted + ElGamalEncryptor(const uint8_t *pub); - 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 - { - public: + uint8_t m_PublicKey[256]; + }; - ElGamalDecryptor (const uint8_t * priv); - bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; // 514 bytes encrypted, 222 bytes data - size_t GetPublicKeyLen () const override { return 256; }; + class ElGamalDecryptor : public CryptoKeyDecryptor // for destination + { + 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 - class ECIESP256Encryptor: public CryptoKeyEncryptor - { - public: + class ECIESP256Encryptor : public CryptoKeyEncryptor { + public: - ECIESP256Encryptor (const uint8_t * pub); - ~ECIESP256Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted) override; + ECIESP256Encryptor(const uint8_t *pub); - private: + ~ECIESP256Encryptor(); - EC_GROUP * m_Curve; - EC_POINT * m_PublicKey; - }; + void Encrypt(const uint8_t *data, uint8_t *encrypted) override; + + private: + + EC_GROUP *m_Curve; + EC_POINT *m_PublicKey; + }; - class ECIESP256Decryptor: public CryptoKeyDecryptor - { - public: + class ECIESP256Decryptor : public CryptoKeyDecryptor { + public: - ECIESP256Decryptor (const uint8_t * priv); - ~ECIESP256Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; - size_t GetPublicKeyLen () const override { return 64; }; + ECIESP256Decryptor(const uint8_t *priv); - private: + ~ECIESP256Decryptor(); - EC_GROUP * m_Curve; - BIGNUM * m_PrivateKey; - }; + bool Decrypt(const uint8_t *encrypted, uint8_t *data) override; - 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 - class ECIESGOSTR3410Encryptor: public CryptoKeyEncryptor - { - public: + class ECIESGOSTR3410Encryptor : public CryptoKeyEncryptor { + public: - ECIESGOSTR3410Encryptor (const uint8_t * pub); - ~ECIESGOSTR3410Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted) override; + ECIESGOSTR3410Encryptor(const uint8_t *pub); - 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 - { - public: + class ECIESGOSTR3410Decryptor : public CryptoKeyDecryptor { + public: - ECIESGOSTR3410Decryptor (const uint8_t * priv); - ~ECIESGOSTR3410Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; - size_t GetPublicKeyLen () const override { return 64; }; + ECIESGOSTR3410Decryptor(const uint8_t *priv); - 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 - class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor - { - public: + class ECIESX25519AEADRatchetEncryptor : public CryptoKeyEncryptor { + public: - ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); - ~ECIESX25519AEADRatchetEncryptor () {}; - void Encrypt (const uint8_t *, uint8_t * pub) override; - // copies m_PublicKey to pub + ECIESX25519AEADRatchetEncryptor(const uint8_t *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 - { - public: + private: - ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false); - ~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 (); }; + uint8_t m_PublicKey[32]; + }; - 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 diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 30635b09..19d70438 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -14,420 +14,375 @@ #include "Destination.h" #include "Datagram.h" -namespace i2p -{ -namespace datagram -{ - DatagramDestination::DatagramDestination (std::shared_ptr owner, bool gzip): - m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip) - { - if (m_Gzip) - m_Deflator.reset (new i2p::data::GzipDeflator); +namespace i2p { + namespace datagram { + DatagramDestination::DatagramDestination(std::shared_ptr owner, bool gzip) : + m_Owner(owner), m_Receiver(nullptr), m_RawReceiver(nullptr), m_Gzip(gzip) { + if (m_Gzip) + m_Deflator.reset(new i2p::data::GzipDeflator); - auto identityLen = m_Owner->GetIdentity ()->GetFullLen (); - m_From.resize (identityLen); - m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen); - m_Signature.resize (m_Owner->GetIdentity ()->GetSignatureLen ()); - } + auto identityLen = m_Owner->GetIdentity()->GetFullLen(); + m_From.resize(identityLen); + m_Owner->GetIdentity()->ToBuffer(m_From.data(), identityLen); + m_Signature.resize(m_Owner->GetIdentity()->GetSignatureLen()); + } - DatagramDestination::~DatagramDestination () - { - m_Sessions.clear(); - } + DatagramDestination::~DatagramDestination() { + m_Sessions.clear(); + } - void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) - { - auto session = ObtainSession(identity); - SendDatagram (session, payload, len, fromPort, toPort); - FlushSendQueue (session); - } + void + DatagramDestination::SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &identity, + uint16_t fromPort, uint16_t toPort) { + auto session = ObtainSession(identity); + 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) - { - auto session = ObtainSession(identity); - SendRawDatagram (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) { + auto session = ObtainSession(identity); + SendRawDatagram(session, payload, len, fromPort, toPort); + FlushSendQueue(session); + } - std::shared_ptr DatagramDestination::GetSession(const i2p::data::IdentHash & ident) - { - return ObtainSession(ident); - } + std::shared_ptr DatagramDestination::GetSession(const i2p::data::IdentHash &ident) { + return ObtainSession(ident); + } - void DatagramDestination::SendDatagram (std::shared_ptr session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) - { - if (session) - { - if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) - { - uint8_t hash[32]; - SHA256(payload, len, hash); - m_Owner->Sign (hash, 32, m_Signature.data ()); - } - else - m_Owner->Sign (payload, len, m_Signature.data ()); + void + DatagramDestination::SendDatagram(std::shared_ptr session, const uint8_t *payload, size_t len, + uint16_t fromPort, uint16_t toPort) { + if (session) { + if (m_Owner->GetIdentity()->GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) { + uint8_t hash[32]; + SHA256(payload, len, hash); + m_Owner->Sign(hash, 32, 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}}, - fromPort, toPort, false, !session->IsRatchets ()); // datagram - session->SendMsg(msg); - } - } + auto msg = CreateDataMessage({{m_From.data(), m_From.size()}, + {m_Signature.data(), m_Signature.size()}, + {payload, len}}, + fromPort, toPort, false, !session->IsRatchets()); // datagram + session->SendMsg(msg); + } + } - void DatagramDestination::SendRawDatagram (std::shared_ptr session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) - { - if (session) - session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ())); // raw - } + void DatagramDestination::SendRawDatagram(std::shared_ptr session, const uint8_t *payload, + size_t len, uint16_t fromPort, uint16_t toPort) { + if (session) + session->SendMsg( + CreateDataMessage({{payload, len}}, fromPort, toPort, true, !session->IsRatchets())); // raw + } - void DatagramDestination::FlushSendQueue (std::shared_ptr session) - { - if (session) - session->FlushSendQueue (); - } + void DatagramDestination::FlushSendQueue(std::shared_ptr session) { + if (session) + session->FlushSendQueue(); + } - void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len) - { - i2p::data::IdentityEx identity; - size_t identityLen = identity.FromBuffer (buf, len); - const uint8_t * signature = buf + identityLen; - size_t headerLen = identityLen + identity.GetSignatureLen (); + void DatagramDestination::HandleDatagram(uint16_t fromPort, uint16_t toPort, uint8_t *const &buf, size_t len) { + i2p::data::IdentityEx identity; + size_t identityLen = identity.FromBuffer(buf, len); + const uint8_t *signature = buf + identityLen; + size_t headerLen = identityLen + identity.GetSignatureLen(); - bool verified = false; - if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) - { - uint8_t hash[32]; - SHA256(buf + headerLen, len - headerLen, hash); - verified = identity.Verify (hash, 32, signature); - } - else - verified = identity.Verify (buf + headerLen, len - headerLen, signature); + bool verified = false; + if (identity.GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) { + uint8_t hash[32]; + SHA256(buf + headerLen, len - headerLen, hash); + verified = identity.Verify(hash, 32, signature); + } else + verified = identity.Verify(buf + headerLen, len - headerLen, signature); - if (verified) - { - auto h = identity.GetIdentHash(); - auto session = ObtainSession(h); - session->Ack(); - auto r = FindReceiver(toPort); - if(r) - r(identity, fromPort, toPort, buf + headerLen, len -headerLen); - else - LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort); - } - else - LogPrint (eLogWarning, "Datagram signature verification failed"); - } + if (verified) { + auto h = identity.GetIdentHash(); + auto session = ObtainSession(h); + session->Ack(); + auto r = FindReceiver(toPort); + if (r) + r(identity, fromPort, toPort, buf + headerLen, len - headerLen); + else + LogPrint(eLogWarning, "DatagramDestination: no receiver for port ", toPort); + } else + LogPrint(eLogWarning, "Datagram signature verification failed"); + } - void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (m_RawReceiver) - m_RawReceiver (fromPort, toPort, buf, len); - else - LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram"); - } + void + DatagramDestination::HandleRawDatagram(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len) { + if (m_RawReceiver) + m_RawReceiver(fromPort, toPort, buf, len); + else + LogPrint(eLogWarning, "DatagramDestination: no receiver for raw datagram"); + } - DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) - { - std::lock_guard lock(m_ReceiversMutex); - Receiver r = m_Receiver; - auto itr = m_ReceiversByPorts.find(port); - if (itr != m_ReceiversByPorts.end()) - r = itr->second; - return r; - } + DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) { + std::lock_guard lock(m_ReceiversMutex); + Receiver r = m_Receiver; + auto itr = m_ReceiversByPorts.find(port); + if (itr != m_ReceiversByPorts.end()) + r = itr->second; + return r; + } - void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw) - { - // unzip it - uint8_t uncompressed[MAX_DATAGRAM_SIZE]; - size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE); - if (uncompressedLen) - { - if (isRaw) - HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen); - else - HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); - } - else - LogPrint (eLogWarning, "Datagram: decompression failed"); - } + void DatagramDestination::HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, + size_t len, bool isRaw) { + // unzip it + uint8_t uncompressed[MAX_DATAGRAM_SIZE]; + size_t uncompressedLen = m_Inflator.Inflate(buf, len, uncompressed, MAX_DATAGRAM_SIZE); + if (uncompressedLen) { + if (isRaw) + HandleRawDatagram(fromPort, toPort, uncompressed, uncompressedLen); + else + HandleDatagram(fromPort, toPort, uncompressed, uncompressedLen); + } else + LogPrint(eLogWarning, "Datagram: decompression failed"); + } - std::shared_ptr DatagramDestination::CreateDataMessage ( - const std::vector >& payloads, - uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) - { - size_t size; - auto msg = m_I2NPMsgsPool.AcquireShared (); - uint8_t * buf = msg->GetPayload (); - buf += 4; // reserve for length + std::shared_ptr DatagramDestination::CreateDataMessage( + const std::vector > &payloads, + uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) { + size_t size; + auto msg = m_I2NPMsgsPool.AcquireShared(); + uint8_t *buf = msg->GetPayload(); + buf += 4; // reserve for length - if (m_Gzip && m_Deflator) - size = m_Deflator->Deflate (payloads, buf, msg->maxLen - msg->len); - else - size = i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); + if (m_Gzip && m_Deflator) + size = m_Deflator->Deflate(payloads, buf, msg->maxLen - msg->len); + else + size = i2p::data::GzipNoCompression(payloads, buf, msg->maxLen - msg->len); - if (size) - { - htobe32buf (msg->GetPayload (), size); // length - htobe16buf (buf + 4, fromPort); // source port - htobe16buf (buf + 6, toPort); // destination port - buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol - msg->len += size + 4; - msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); - } - else - msg = nullptr; - return msg; - } + if (size) { + htobe32buf(msg->GetPayload(), size); // length + htobe16buf(buf + 4, fromPort); // source port + htobe16buf(buf + 6, toPort); // destination port + buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW + : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol + msg->len += size + 4; + msg->FillI2NPMessageHeader(eI2NPData, 0, checksum); + } else + msg = nullptr; + return msg; + } - void DatagramDestination::CleanUp () - { - if (m_Sessions.empty ()) return; - auto now = i2p::util::GetMillisecondsSinceEpoch(); - LogPrint(eLogDebug, "DatagramDestination: clean up sessions"); - std::unique_lock lock(m_SessionsMutex); - // for each session ... - for (auto it = m_Sessions.begin (); it != m_Sessions.end (); ) - { - // check if expired - if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE) - { - LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32()); - it->second->Stop (); - it = m_Sessions.erase (it); // we are expired - } - else - it++; - } - } + void DatagramDestination::CleanUp() { + if (m_Sessions.empty()) return; + auto now = i2p::util::GetMillisecondsSinceEpoch(); + LogPrint(eLogDebug, "DatagramDestination: clean up sessions"); + std::unique_lock lock(m_SessionsMutex); + // for each session ... + for (auto it = m_Sessions.begin(); it != m_Sessions.end();) { + // check if expired + if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE) { + LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32()); + it->second->Stop(); + it = m_Sessions.erase(it); // we are expired + } else + it++; + } + } - std::shared_ptr DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity) - { - std::shared_ptr session = nullptr; - std::lock_guard lock(m_SessionsMutex); - auto itr = m_Sessions.find(identity); - if (itr == m_Sessions.end()) { - // not found, create new session - session = std::make_shared(m_Owner, identity); - session->Start (); - m_Sessions[identity] = session; - } else { - session = itr->second; - } - return session; - } + std::shared_ptr DatagramDestination::ObtainSession(const i2p::data::IdentHash &identity) { + std::shared_ptr session = nullptr; + std::lock_guard lock(m_SessionsMutex); + auto itr = m_Sessions.find(identity); + if (itr == m_Sessions.end()) { + // not found, create new session + session = std::make_shared(m_Owner, identity); + session->Start(); + m_Sessions[identity] = session; + } else { + session = itr->second; + } + return session; + } - std::shared_ptr DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote) - { - std::lock_guard lock(m_SessionsMutex); - for ( auto & item : m_Sessions) - { - if(item.first == remote) return std::make_shared(item.second->GetSessionInfo()); - } - return nullptr; - } + std::shared_ptr + DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash &remote) { + std::lock_guard lock(m_SessionsMutex); + for (auto &item: m_Sessions) { + if (item.first == remote) return std::make_shared(item.second->GetSessionInfo()); + } + return nullptr; + } - DatagramSession::DatagramSession(std::shared_ptr localDestination, - const i2p::data::IdentHash & remoteIdent) : - m_LocalDestination(localDestination), - m_RemoteIdent(remoteIdent), - m_RequestingLS(false) - { - } + DatagramSession::DatagramSession(std::shared_ptr localDestination, + const i2p::data::IdentHash &remoteIdent) : + m_LocalDestination(localDestination), + m_RemoteIdent(remoteIdent), + m_RequestingLS(false) { + } - void DatagramSession::Start () - { - m_LastUse = i2p::util::GetMillisecondsSinceEpoch (); - } + void DatagramSession::Start() { + m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); + } - void DatagramSession::Stop () - { - } + void DatagramSession::Stop() { + } - void DatagramSession::SendMsg(std::shared_ptr msg) - { - // we used this session - m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); - if (msg || m_SendQueue.empty ()) - m_SendQueue.push_back(msg); - // flush queue right away if full - if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) - FlushSendQueue(); - } + void DatagramSession::SendMsg(std::shared_ptr msg) { + // we used this session + m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); + if (msg || m_SendQueue.empty()) + m_SendQueue.push_back(msg); + // flush queue right away if full + if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) + FlushSendQueue(); + } - DatagramSession::Info DatagramSession::GetSessionInfo() const - { - if(!m_RoutingSession) - return DatagramSession::Info(nullptr, nullptr, m_LastUse); + DatagramSession::Info DatagramSession::GetSessionInfo() const { + if (!m_RoutingSession) + return DatagramSession::Info(nullptr, nullptr, m_LastUse); - auto routingPath = m_RoutingSession->GetSharedRoutingPath(); - if (!routingPath) - return DatagramSession::Info(nullptr, nullptr, m_LastUse); - auto lease = routingPath->remoteLease; - auto tunnel = routingPath->outboundTunnel; - if(lease) - { - if(tunnel) - return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse); - else - return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse); - } - else if(tunnel) - return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse); - else - return DatagramSession::Info(nullptr, nullptr, m_LastUse); - } + auto routingPath = m_RoutingSession->GetSharedRoutingPath(); + if (!routingPath) + return DatagramSession::Info(nullptr, nullptr, m_LastUse); + auto lease = routingPath->remoteLease; + auto tunnel = routingPath->outboundTunnel; + if (lease) { + if (tunnel) + return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse); + else + return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse); + } else if (tunnel) + return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse); + else + return DatagramSession::Info(nullptr, nullptr, m_LastUse); + } - void DatagramSession::Ack() - { - m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); - auto path = GetSharedRoutingPath(); - if(path) - path->updateTime = i2p::util::GetSecondsSinceEpoch (); - if (IsRatchets ()) - SendMsg (nullptr); // send empty message in case if we have some data to send - } + void DatagramSession::Ack() { + m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); + auto path = GetSharedRoutingPath(); + if (path) + path->updateTime = i2p::util::GetSecondsSinceEpoch(); + if (IsRatchets()) + SendMsg(nullptr); // send empty message in case if we have some data to send + } - std::shared_ptr DatagramSession::GetSharedRoutingPath () - { - if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) - { - m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); - if (!m_RemoteLeaseSet) - { - if(!m_RequestingLS) - { - m_RequestingLS = true; - m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); - } - return nullptr; - } - } + std::shared_ptr DatagramSession::GetSharedRoutingPath() { + if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired()) { + m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); + if (!m_RemoteLeaseSet) { + if (!m_RequestingLS) { + m_RequestingLS = true; + m_LocalDestination->RequestDestination(m_RemoteIdent, + std::bind(&DatagramSession::HandleLeaseSetUpdated, this, + std::placeholders::_1)); + } + return nullptr; + } + } - if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) - { - bool found = false; - for (auto& it: m_PendingRoutingSessions) - if (it->GetOwner () && m_RoutingSession->IsReadyToSend ()) // found established session - { - m_RoutingSession = it; - m_PendingRoutingSessions.clear (); - found = true; - break; - } - if (!found) - { - m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); - if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) - m_PendingRoutingSessions.push_back (m_RoutingSession); - } - } + if (!m_RoutingSession || m_RoutingSession->IsTerminated() || !m_RoutingSession->IsReadyToSend()) { + bool found = false; + for (auto &it: m_PendingRoutingSessions) + if (it->GetOwner() && m_RoutingSession->IsReadyToSend()) // found established session + { + m_RoutingSession = it; + m_PendingRoutingSessions.clear(); + found = true; + break; + } + if (!found) { + m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); + if (!m_RoutingSession->GetOwner() || !m_RoutingSession->IsReadyToSend()) + m_PendingRoutingSessions.push_back(m_RoutingSession); + } + } - auto path = m_RoutingSession->GetSharedRoutingPath(); - if (path && m_RoutingSession->IsRatchets () && - m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT) - { - m_RoutingSession->SetSharedRoutingPath (nullptr); - path = nullptr; - } + auto path = m_RoutingSession->GetSharedRoutingPath(); + if (path && m_RoutingSession->IsRatchets() && + m_LastUse > m_RoutingSession->GetLastActivityTimestamp() * 1000 + DATAGRAM_SESSION_PATH_TIMEOUT) { + m_RoutingSession->SetSharedRoutingPath(nullptr); + path = nullptr; + } - if (path) - { - if (path->outboundTunnel && !path->outboundTunnel->IsEstablished ()) - { - // bad outbound tunnel, switch outbound tunnel - path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(path->outboundTunnel); - if (!path->outboundTunnel) - m_RoutingSession->SetSharedRoutingPath (nullptr); - } + if (path) { + if (path->outboundTunnel && !path->outboundTunnel->IsEstablished()) { + // bad outbound tunnel, switch outbound tunnel + path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel( + path->outboundTunnel); + if (!path->outboundTunnel) + m_RoutingSession->SetSharedRoutingPath(nullptr); + } - if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) - { - // bad lease, switch to next one - if (m_RemoteLeaseSet) - { - auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( - [&](const i2p::data::Lease& l) -> bool - { - return l.tunnelID == path->remoteLease->tunnelID; - }); - auto sz = ls.size(); - if (sz) - { - auto idx = rand() % sz; - path->remoteLease = ls[idx]; - } - else - m_RoutingSession->SetSharedRoutingPath (nullptr); - } - else - { - // no remote lease set? - LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32()); - m_RoutingSession->SetSharedRoutingPath (nullptr); - } - } - } - else - { - // no current path, make one - path = std::make_shared(); + if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + // bad lease, switch to next one + if (m_RemoteLeaseSet) { + auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( + [&](const i2p::data::Lease &l) -> bool { + return l.tunnelID == path->remoteLease->tunnelID; + }); + auto sz = ls.size(); + if (sz) { + auto idx = rand() % sz; + path->remoteLease = ls[idx]; + } else + m_RoutingSession->SetSharedRoutingPath(nullptr); + } else { + // no remote lease set? + LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", + m_RemoteIdent.ToBase32()); + m_RoutingSession->SetSharedRoutingPath(nullptr); + } + } + } else { + // no current path, make one + path = std::make_shared(); - if (m_RemoteLeaseSet) - { - // pick random next good lease - auto ls = m_RemoteLeaseSet->GetNonExpiredLeases(); - auto sz = ls.size(); - if (sz) - { - auto idx = rand() % sz; - path->remoteLease = ls[idx]; - } - else - return nullptr; + if (m_RemoteLeaseSet) { + // pick random next good lease + auto ls = m_RemoteLeaseSet->GetNonExpiredLeases(); + auto sz = ls.size(); + if (sz) { + auto idx = rand() % sz; + path->remoteLease = ls[idx]; + } else + return nullptr; - auto leaseRouter = i2p::data::netdb.FindRouter (path->remoteLease->tunnelGateway); - path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(nullptr, - leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); - if (!path->outboundTunnel) return nullptr; - } - else - { - // no remote lease set currently, bail - LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32()); - return nullptr; - } - m_RoutingSession->SetSharedRoutingPath(path); - } - return path; - } + auto leaseRouter = i2p::data::netdb.FindRouter(path->remoteLease->tunnelGateway); + path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(nullptr, + leaseRouter + ? leaseRouter->GetCompatibleTransports( + false) + : (i2p::data::RouterInfo::CompatibleTransports) i2p::data::RouterInfo::eAllTransports); + if (!path->outboundTunnel) return nullptr; + } else { + // no remote lease set currently, bail + LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32()); + return nullptr; + } + m_RoutingSession->SetSharedRoutingPath(path); + } + return path; + } - void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) - { - m_RequestingLS = false; - if(!ls) return; - // only update lease set if found and newer than previous lease set - uint64_t oldExpire = 0; - if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); - if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; - } + void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) { + m_RequestingLS = false; + if (!ls) return; + // only update lease set if found and newer than previous lease set + uint64_t oldExpire = 0; + if (m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); + if (ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; + } - void DatagramSession::FlushSendQueue () - { - if (m_SendQueue.empty ()) return; - std::vector send; - auto routingPath = GetSharedRoutingPath(); - // if we don't have a routing path we will drop all queued messages - if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease) - { - for (const auto & msg : m_SendQueue) - { - auto m = m_RoutingSession->WrapSingleMessage(msg); - if (m) - send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); - } - routingPath->outboundTunnel->SendTunnelDataMsg(send); - } - m_SendQueue.clear(); - } -} + void DatagramSession::FlushSendQueue() { + if (m_SendQueue.empty()) return; + std::vector send; + auto routingPath = GetSharedRoutingPath(); + // if we don't have a routing path we will drop all queued messages + if (routingPath && routingPath->outboundTunnel && routingPath->remoteLease) { + for (const auto &msg: m_SendQueue) { + auto m = m_RoutingSession->WrapSingleMessage(msg); + if (m) + send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel, + routingPath->remoteLease->tunnelGateway, + routingPath->remoteLease->tunnelID, m}); + } + routingPath->outboundTunnel->SendTunnelDataMsg(send); + } + m_SendQueue.clear(); + } + } } diff --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index a55c8edf..b53b8b4c 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -20,155 +20,184 @@ #include "I2NPProtocol.h" #include "Garlic.h" -namespace i2p -{ -namespace client -{ - class ClientDestination; -} -namespace datagram -{ - // milliseconds for max session idle time - 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 - const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000; - // milliseconds interval a routing path is used before switching - const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000; - // milliseconds before lease expire should we try switching leases - const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 30 * 1000; - // milliseconds fudge factor for leases handover - const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; - // milliseconds minimum time between path switches - const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; - // max 64 messages buffered in send queue for each datagram session - const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; +namespace i2p { + namespace client { + class ClientDestination; + } + namespace datagram { + // milliseconds for max session idle time + 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 + const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000; + // milliseconds interval a routing path is used before switching + const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000; + // milliseconds before lease expire should we try switching leases + const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 30 * 1000; + // milliseconds fudge factor for leases handover + const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; + // milliseconds minimum time between path switches + const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; + // max 64 messages buffered in send queue for each datagram session + const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; - class DatagramSession : public std::enable_shared_from_this - { + class DatagramSession : public std::enable_shared_from_this { - public: + public: - DatagramSession(std::shared_ptr localDestination, const i2p::data::IdentHash & remoteIdent); + DatagramSession(std::shared_ptr localDestination, + const i2p::data::IdentHash &remoteIdent); - void Start (); - void Stop (); + void Start(); + + void Stop(); - /** @brief ack the garlic routing path */ - void Ack(); + /** @brief ack the garlic routing path */ + void Ack(); - /** send an i2np message to remote endpoint for this session */ - void SendMsg(std::shared_ptr msg); - void FlushSendQueue(); - /** get the last time in milliseconds for when we used this datagram session */ - uint64_t LastActivity() const { return m_LastUse; } + /** send an i2np message to remote endpoint for this session */ + void SendMsg(std::shared_ptr msg); - bool IsRatchets () const { return m_RoutingSession && m_RoutingSession->IsRatchets (); } + void FlushSendQueue(); - struct Info - { - std::shared_ptr IBGW; - std::shared_ptr OBEP; - const uint64_t activity; + /** get the last time in milliseconds for when we used this datagram session */ + uint64_t LastActivity() const { return m_LastUse; } - Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} - Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) : - activity(a) { - if(ibgw) IBGW = std::make_shared(ibgw); - else IBGW = nullptr; - if(obep) OBEP = std::make_shared(obep); - else OBEP = nullptr; - } - }; + bool IsRatchets() const { return m_RoutingSession && m_RoutingSession->IsRatchets(); } - Info GetSessionInfo() const; + struct Info { + std::shared_ptr IBGW; + std::shared_ptr OBEP; + const uint64_t activity; - private: + Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} - std::shared_ptr GetSharedRoutingPath(); + Info(const uint8_t *ibgw, const uint8_t *obep, const uint64_t a) : + activity(a) { + if (ibgw) IBGW = std::make_shared(ibgw); + else IBGW = nullptr; + if (obep) OBEP = std::make_shared(obep); + else OBEP = nullptr; + } + }; - void HandleLeaseSetUpdated(std::shared_ptr ls); + Info GetSessionInfo() const; - private: + private: - std::shared_ptr m_LocalDestination; - i2p::data::IdentHash m_RemoteIdent; - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_RoutingSession; - std::vector > m_PendingRoutingSessions; - std::vector > m_SendQueue; - uint64_t m_LastUse; - bool m_RequestingLS; - }; + std::shared_ptr GetSharedRoutingPath(); - typedef std::shared_ptr DatagramSession_ptr; + void HandleLeaseSetUpdated(std::shared_ptr ls); - const size_t MAX_DATAGRAM_SIZE = 32768; - class DatagramDestination - { - typedef std::function Receiver; - typedef std::function RawReceiver; + private: - public: + std::shared_ptr m_LocalDestination; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_RoutingSession; + std::vector > m_PendingRoutingSessions; + std::vector > m_SendQueue; + uint64_t m_LastUse; + bool m_RequestingLS; + }; - DatagramDestination (std::shared_ptr owner, bool gzip); - ~DatagramDestination (); + typedef std::shared_ptr DatagramSession_ptr; - void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); - void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); - // TODO: implement calls from other thread from SAM + const size_t MAX_DATAGRAM_SIZE = 32768; - std::shared_ptr GetSession(const i2p::data::IdentHash & ident); - void SendDatagram (std::shared_ptr session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); - void SendRawDatagram (std::shared_ptr session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); - void FlushSendQueue (std::shared_ptr session); + class DatagramDestination { + typedef std::function Receiver; + 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; }; - void ResetReceiver () { m_Receiver = nullptr; }; + DatagramDestination(std::shared_ptr owner, bool gzip); - void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; }; - void ResetReceiver (uint16_t port) { std::lock_guard lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); }; + ~DatagramDestination(); - void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; }; - void ResetRawReceiver () { m_RawReceiver = nullptr; }; + void + SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident, uint16_t fromPort = 0, + uint16_t toPort = 0); - std::shared_ptr 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 - void CleanUp (); + std::shared_ptr GetSession(const i2p::data::IdentHash &ident); - private: + void SendDatagram(std::shared_ptr session, const uint8_t *payload, size_t len, + uint16_t fromPort, uint16_t toPort); - std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); + void SendRawDatagram(std::shared_ptr session, const uint8_t *payload, size_t len, + uint16_t fromPort, uint16_t toPort); - std::shared_ptr CreateDataMessage (const std::vector >& payloads, - uint16_t fromPort, uint16_t toPort, bool isRaw = false, bool checksum = true); + void FlushSendQueue(std::shared_ptr session); - 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); + void HandleDataMessagePayload(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 */ - Receiver FindReceiver(uint16_t port); + void SetReceiver(const Receiver &receiver) { m_Receiver = receiver; }; - private: + void ResetReceiver() { m_Receiver = nullptr; }; - std::shared_ptr m_Owner; - Receiver m_Receiver; // default - RawReceiver m_RawReceiver; // default - bool m_Gzip; // gzip compression of data messages - std::mutex m_SessionsMutex; - std::map m_Sessions; - std::mutex m_ReceiversMutex; - std::map m_ReceiversByPorts; + void SetReceiver(const Receiver &receiver, uint16_t port) { + std::lock_guard lock(m_ReceiversMutex); + m_ReceiversByPorts[port] = receiver; + }; - i2p::data::GzipInflator m_Inflator; - std::unique_ptr m_Deflator; - std::vector m_From, m_Signature; - i2p::util::MemoryPool > m_I2NPMsgsPool; - }; -} + void ResetReceiver(uint16_t port) { + std::lock_guard lock(m_ReceiversMutex); + m_ReceiversByPorts.erase(port); + }; + + void SetRawReceiver(const RawReceiver &receiver) { m_RawReceiver = receiver; }; + + void ResetRawReceiver() { m_RawReceiver = nullptr; }; + + std::shared_ptr GetInfoForRemote(const i2p::data::IdentHash &remote); + + // clean up stale sessions + void CleanUp(); + + private: + + std::shared_ptr ObtainSession(const i2p::data::IdentHash &ident); + + std::shared_ptr + CreateDataMessage(const std::vector > &payloads, + uint16_t fromPort, uint16_t toPort, bool isRaw = false, bool checksum = true); + + void HandleDatagram(uint16_t fromPort, uint16_t toPort, uint8_t *const &buf, size_t len); + + void HandleRawDatagram(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len); + + /** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */ + Receiver FindReceiver(uint16_t port); + + private: + + std::shared_ptr m_Owner; + Receiver m_Receiver; // default + RawReceiver m_RawReceiver; // default + bool m_Gzip; // gzip compression of data messages + std::mutex m_SessionsMutex; + std::map m_Sessions; + std::mutex m_ReceiversMutex; + std::map m_ReceiversByPorts; + + i2p::data::GzipInflator m_Inflator; + std::unique_ptr m_Deflator; + std::vector m_From, m_Signature; + i2p::util::MemoryPool > m_I2NPMsgsPool; + }; + } } #endif diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 719f5830..bd9928ab 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -19,1382 +19,1235 @@ #include "NetDb.hpp" #include "Destination.h" -namespace i2p -{ -namespace client -{ - LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service, - bool isPublic, const std::map * params): - m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), - m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), - m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service), - m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) - { - int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH; - int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY; - int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; - int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY; - int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; - int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; - int numTags = DEFAULT_TAGS_TO_SEND; - std::shared_ptr > explicitPeers; - try - { - if (params) - { - auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH); - if (it != params->end ()) - inLen = std::stoi(it->second); - it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH); - if (it != params->end ()) - outLen = std::stoi(it->second); - it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY); - if (it != params->end ()) - inQty = std::stoi(it->second); - it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY); - if (it != params->end ()) - outQty = std::stoi(it->second); - it = params->find (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE); - if (it != params->end ()) - inVar = std::stoi(it->second); - it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE); - if (it != params->end ()) - outVar = std::stoi(it->second); - it = params->find (I2CP_PARAM_TAGS_TO_SEND); - if (it != params->end ()) - numTags = std::stoi(it->second); - LogPrint (eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags"); - it = params->find (I2CP_PARAM_RATCHET_INBOUND_TAGS); - if (it != params->end ()) - SetNumRatchetInboundTags (std::stoi(it->second)); - it = params->find (I2CP_PARAM_EXPLICIT_PEERS); - if (it != params->end ()) - { - explicitPeers = std::make_shared >(); - std::stringstream ss(it->second); - std::string b64; - while (std::getline (ss, b64, ',')) - { - i2p::data::IdentHash ident; - ident.FromBase64 (b64); - explicitPeers->push_back (ident); - LogPrint (eLogInfo, "Destination: Added to explicit peers list: ", b64); - } - } - it = params->find (I2CP_PARAM_INBOUND_NICKNAME); - if (it != params->end ()) m_Nickname = it->second; - else // try outbound - { - it = params->find (I2CP_PARAM_OUTBOUND_NICKNAME); - if (it != params->end ()) m_Nickname = it->second; - // otherwise we set default nickname in Start when we know local address - } - it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET); - if (it != params->end ()) - { - // oveeride isPublic - m_IsPublic = (it->second != "true"); - } - it = params->find (I2CP_PARAM_LEASESET_TYPE); - if (it != params->end ()) - m_LeaseSetType = std::stoi(it->second); - if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) - { - // authentication for encrypted LeaseSet - it = params->find (I2CP_PARAM_LEASESET_AUTH_TYPE); - if (it != params->end ()) - { - auto authType = std::stoi (it->second); - if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) - m_AuthType = authType; - else - LogPrint (eLogError, "Destination: Unknown auth type ", authType); - } - } - it = params->find (I2CP_PARAM_LEASESET_PRIV_KEY); - if (it != params->end ()) - { - m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>()); - if (m_LeaseSetPrivKey->FromBase64 (it->second) != 32) - { - LogPrint(eLogError, "Destination: Invalid value i2cp.leaseSetPrivKey ", it->second); - m_LeaseSetPrivKey.reset (nullptr); - } - } - } - } - catch (std::exception & ex) - { - LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); - } - SetNumTags (numTags); - m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar); - if (explicitPeers) - m_Pool->SetExplicitPeers (explicitPeers); - if(params) - { - auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY); - if (itr != params->end()) { - auto maxlatency = std::stoi(itr->second); - itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY); - if (itr != params->end()) { - auto minlatency = std::stoi(itr->second); - if ( minlatency > 0 && maxlatency > 0 ) { - // set tunnel pool latency - LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]"); - m_Pool->RequireLatency(minlatency, maxlatency); - } - } - } - } - } - - LeaseSetDestination::~LeaseSetDestination () - { - if (m_Pool) - i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); - for (auto& it: m_LeaseSetRequests) - it.second->Complete (nullptr); - } - - void LeaseSetDestination::Start () - { - if (m_Nickname.empty ()) - m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname - LoadTags (); - m_Pool->SetLocalDestination (shared_from_this ()); - m_Pool->SetActive (true); - m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); - m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, - shared_from_this (), std::placeholders::_1)); - } - - void LeaseSetDestination::Stop () - { - m_CleanupTimer.cancel (); - m_PublishConfirmationTimer.cancel (); - m_PublishVerificationTimer.cancel (); - if (m_Pool) - { - m_Pool->SetLocalDestination (nullptr); - i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } - SaveTags (); - CleanUp (); // GarlicDestination - } - - bool LeaseSetDestination::Reconfigure(std::map params) - { - auto itr = params.find("i2cp.dontPublishLeaseSet"); - if (itr != params.end()) - { - m_IsPublic = itr->second != "true"; - } - - int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency; - std::map intOpts = { - {I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen}, - {I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen}, - {I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant}, - {I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant}, - {I2CP_PARAM_TAGS_TO_SEND, numTags}, - {I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency}, - {I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency} - }; - - auto pool = GetTunnelPool(); - inLen = pool->GetNumInboundHops(); - outLen = pool->GetNumOutboundHops(); - inQuant = pool->GetNumInboundTunnels(); - outQuant = pool->GetNumOutboundTunnels(); - minLatency = 0; - maxLatency = 0; - - for (auto & opt : intOpts) - { - itr = params.find(opt.first); - if(itr != params.end()) - { - opt.second = std::stoi(itr->second); - } - } - pool->RequireLatency(minLatency, maxLatency); - return pool->Reconfigure(inLen, outLen, inQuant, outQuant); - } - - std::shared_ptr LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) - { - std::shared_ptr remoteLS; - { - std::lock_guard lock(m_RemoteLeaseSetsMutex); - auto it = m_RemoteLeaseSets.find (ident); - if (it != m_RemoteLeaseSets.end ()) - remoteLS = it->second; - } - - if (remoteLS) - { - if (!remoteLS->IsExpired ()) - { - if (remoteLS->ExpiresSoon()) - { - LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire"); - // update now before expiration for smooth handover - auto s = shared_from_this (); - RequestDestination(ident, [s, ident] (std::shared_ptr ls) { - if(ls && !ls->IsExpired()) - { - ls->PopulateLeases(); - { - std::lock_guard _lock(s->m_RemoteLeaseSetsMutex); - s->m_RemoteLeaseSets[ident] = ls; - } - } - }); - } - return remoteLS; - } - else - { - LogPrint (eLogWarning, "Destination: Remote LeaseSet expired"); - std::lock_guard lock(m_RemoteLeaseSetsMutex); - m_RemoteLeaseSets.erase (ident); - return nullptr; - } - } - else - { - auto ls = i2p::data::netdb.FindLeaseSet (ident); - if (ls && !ls->IsExpired ()) - { - ls->PopulateLeases (); // since we don't store them in netdb - std::lock_guard _lock(m_RemoteLeaseSetsMutex); - m_RemoteLeaseSets[ident] = ls; - return ls; - } - } - return nullptr; - } - - std::shared_ptr LeaseSetDestination::GetLeaseSet () - { - if (!m_Pool) return nullptr; - if (!m_LeaseSet) - UpdateLeaseSet (); - auto ls = GetLeaseSetMt (); - return (ls && ls->GetInnerLeaseSet ()) ? ls->GetInnerLeaseSet () : ls; // always non-encrypted - } - - std::shared_ptr LeaseSetDestination::GetLeaseSetMt () - { - std::lock_guard l(m_LeaseSetMutex); - return m_LeaseSet; - } - - void LeaseSetDestination::SetLeaseSet (std::shared_ptr newLeaseSet) - { - { - std::lock_guard l(m_LeaseSetMutex); - m_LeaseSet = newLeaseSet; - } - i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); - if (m_IsPublic) - { - auto s = shared_from_this (); - m_Service.post ([s](void) - { - s->m_PublishVerificationTimer.cancel (); - s->Publish (); - }); - } - } - - void LeaseSetDestination::UpdateLeaseSet () - { - int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels - if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum - auto tunnels = m_Pool->GetInboundTunnels (numTunnels); - if (!tunnels.empty ()) - CreateNewLeaseSet (tunnels); - else - LogPrint (eLogInfo, "Destination: No inbound tunnels for LeaseSet"); - } - - bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) - { - struct - { - uint8_t k[32], t[32]; - } data; - memcpy (data.k, key, 32); - memcpy (data.t, tag, 32); - auto s = shared_from_this (); - m_Service.post ([s,data](void) - { - s->AddSessionKey (data.k, data.t); - }); - return true; - } - - void LeaseSetDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) - { - struct - { - uint8_t k[32]; - uint64_t t; - } data; - memcpy (data.k, key, 32); - data.t = tag; - auto s = shared_from_this (); - m_Service.post ([s,data](void) - { - s->AddECIESx25519Key (data.k, data.t); - }); - } - - void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr msg) - { - m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg)); - } - - void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) - { - uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); - m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID)); - } - - void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) - { - I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]); - uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET); - LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID); - } - - bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) - { - switch (typeID) - { - case eI2NPData: - HandleDataMessage (payload, len); - break; - case eI2NPDeliveryStatus: - // we assume tunnel tests non-encrypted - HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); - break; - case eI2NPDatabaseStore: - HandleDatabaseStoreMessage (payload, len); - break; - case eI2NPDatabaseSearchReply: - HandleDatabaseSearchReplyMessage (payload, len); - break; - case eI2NPShortTunnelBuildReply: // might come as garlic encrypted - i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID)); - break; - default: - LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID); - return false; - } - return true; - } - - void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) - { - uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); - size_t offset = DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore"); - offset += 36; - } - i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET); - std::shared_ptr leaseSet; - switch (buf[DATABASE_STORE_TYPE_OFFSET]) - { - case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1 - case i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2: // 3 - { - LogPrint (eLogDebug, "Destination: Remote LeaseSet"); - std::lock_guard lock(m_RemoteLeaseSetsMutex); - auto it = m_RemoteLeaseSets.find (key); - if (it != m_RemoteLeaseSets.end () && - it->second->GetStoreType () == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type - { - leaseSet = it->second; - if (leaseSet->IsNewer (buf + offset, len - offset)) - { - leaseSet->Update (buf + offset, len - offset); - if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ()) - LogPrint (eLogDebug, "Destination: Remote LeaseSet updated"); - else - { - LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed"); - m_RemoteLeaseSets.erase (it); - leaseSet = nullptr; - } - } - else - LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated"); - } - else - { - // add or replace - if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) - leaseSet = std::make_shared (buf + offset, len - offset); // LeaseSet - else - leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2 - if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ()) - { - if (leaseSet->GetIdentHash () != GetIdentHash ()) - { - LogPrint (eLogDebug, "Destination: New remote LeaseSet added"); - m_RemoteLeaseSets[key] = leaseSet; - } - else - LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped"); - } - else - { - LogPrint (eLogError, "Destination: New remote LeaseSet failed"); - leaseSet = nullptr; - } - } - break; - } - case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 - { - auto it2 = m_LeaseSetRequests.find (key); - if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey) - { - auto ls2 = std::make_shared (buf + offset, len - offset, - it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ()); - if (ls2->IsValid ()) - { - m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key - m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup - leaseSet = ls2; - } - } - else - LogPrint (eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2"); - break; - } - default: - LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped"); - } - - auto it1 = m_LeaseSetRequests.find (key); - if (it1 != m_LeaseSetRequests.end ()) - { - it1->second->requestTimeoutTimer.cancel (); - if (it1->second) it1->second->Complete (leaseSet); - m_LeaseSetRequests.erase (it1); - } - } - - void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len) - { - i2p::data::IdentHash key (buf); - int num = buf[32]; // num - LogPrint (eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64 (), " num=", num); - auto it = m_LeaseSetRequests.find (key); - if (it != m_LeaseSetRequests.end ()) - { - auto request = it->second; - bool found = false; - if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) - { - for (int i = 0; i < num; i++) - { - i2p::data::IdentHash peerHash (buf + 33 + i*32); - if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash)) - { - LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); - i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory - } - } - - auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded); - if (floodfill) - { - LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ()); - if (SendLeaseSetRequest (key, floodfill, request)) - found = true; - } - } - if (!found) - { - LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills"); - request->Complete (nullptr); - m_LeaseSetRequests.erase (key); - } - } - else - LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); - } - - void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) - { - if (msgID == m_PublishReplyToken) - { - LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32()); - m_ExcludedFloodfills.clear (); - m_PublishReplyToken = 0; - // schedule verification - m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); - m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, - shared_from_this (), std::placeholders::_1)); - } - else - i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); - } - - void LeaseSetDestination::SetLeaseSetUpdated () - { - UpdateLeaseSet (); - } - - void LeaseSetDestination::Publish () - { - auto leaseSet = GetLeaseSetMt (); - if (!leaseSet || !m_Pool) - { - LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet"); - return; - } - if (m_PublishReplyToken) - { - LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending"); - return; - } - auto ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL) - { - LogPrint (eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, " seconds"); - m_PublishDelayTimer.cancel (); - m_PublishDelayTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_MIN_INTERVAL)); - m_PublishDelayTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishDelayTimer, - shared_from_this (), std::placeholders::_1)); - return; - } - if (!m_Pool->GetInboundTunnels ().size () || !m_Pool->GetOutboundTunnels ().size ()) - { - LogPrint (eLogError, "Destination: Can't publish LeaseSet. Destination is not ready"); - return; - } - auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); - if (!floodfill) - { - LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); - m_ExcludedFloodfills.clear (); - return; - } - auto outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); - auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); - if (!outbound || !inbound) - { - LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); - m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); - floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); - if (floodfill) - { - outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); - if (outbound) - { - inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); - if (!inbound) - LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); - } - else - LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); - } - else - LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); - if (!floodfill || !outbound || !inbound) - { - m_ExcludedFloodfills.clear (); - return; - } - } - m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); - LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); - RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); - auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound)); - m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); - m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, - shared_from_this (), std::placeholders::_1)); - outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); - m_LastSubmissionTime = ts; - } - - void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_PublishReplyToken) - { - m_PublishReplyToken = 0; - if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) - { - LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again"); - Publish (); - } - else - { - LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); - // Java floodfill never sends confirmation back for unknown crypto type - // assume it successive and try to verify - m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); - m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, - shared_from_this (), std::placeholders::_1)); - - } - } - } - } - - void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ls = GetLeaseSetMt (); - if (!ls) - { - LogPrint (eLogWarning, "Destination: Couldn't verify LeaseSet for ", GetIdentHash().ToBase32()); - return; - } - auto s = shared_from_this (); - RequestLeaseSet (ls->GetStoreHash (), - [s, ls](std::shared_ptr leaseSet) - { - if (leaseSet) - { - if (*ls == *leaseSet) - { - // we got latest LeasetSet - LogPrint (eLogDebug, "Destination: Published LeaseSet verified for ", s->GetIdentHash().ToBase32()); - s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); - s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); - return; - } - else - LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", s->GetIdentHash().ToBase32()); - } - else - LogPrint (eLogWarning, "Destination: Couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32()); - // we have to publish again - s->Publish (); - }); - } - } - - void LeaseSetDestination::HandlePublishDelayTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - Publish (); - } - - bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete) - { - if (!m_Pool || !IsReady ()) - { - if (requestComplete) - m_Service.post ([requestComplete](void){requestComplete (nullptr);}); - return false; - } - m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr)); - return true; - } - - bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete) - { - if (!dest || !m_Pool || !IsReady ()) - { - if (requestComplete) - m_Service.post ([requestComplete](void){requestComplete (nullptr);}); - return false; - } - auto storeHash = dest->GetStoreHash (); - auto leaseSet = FindLeaseSet (storeHash); - if (leaseSet) - { - if (requestComplete) - m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);}); - return true; - } - m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest)); - return true; - } - - void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify) - { - auto s = shared_from_this (); - m_Service.post ([dest, notify, s](void) - { - auto it = s->m_LeaseSetRequests.find (dest); - if (it != s->m_LeaseSetRequests.end ()) - { - auto requestComplete = it->second; - s->m_LeaseSetRequests.erase (it); - if (notify && requestComplete) requestComplete->Complete (nullptr); - } - }); - } - - void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify) - { - if (dest) - CancelDestinationRequest (dest->GetStoreHash (), notify); - } - - void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey) - { - std::set excluded; - auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); - if (floodfill) - { - auto request = std::make_shared (m_Service); - request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2 - if (requestComplete) - request->requestComplete.push_back (requestComplete); - auto ts = i2p::util::GetSecondsSinceEpoch (); - auto ret = m_LeaseSetRequests.insert (std::pair >(dest,request)); - if (ret.second) // inserted - { - request->requestTime = ts; - if (!SendLeaseSetRequest (dest, floodfill, request)) - { - // request failed - m_LeaseSetRequests.erase (ret.first); - if (requestComplete) requestComplete (nullptr); - } - } - else // duplicate - { - LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already"); - if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) - { - // something went wrong - m_LeaseSetRequests.erase (ret.first); - if (requestComplete) requestComplete (nullptr); - } - else if (requestComplete) - ret.first->second->requestComplete.push_back (requestComplete); - } - } - else - { - LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found"); - if (requestComplete) requestComplete (nullptr); - } - } - - bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, - std::shared_ptr nextFloodfill, std::shared_ptr request) - { - if (!request->replyTunnel || !request->replyTunnel->IsEstablished ()) - request->replyTunnel = m_Pool->GetNextInboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (true)); - if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found"); - if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ()) - request->outboundTunnel = m_Pool->GetNextOutboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (false)); - if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found"); - - if (request->replyTunnel && request->outboundTunnel) - { - request->excluded.insert (nextFloodfill->GetIdentHash ()); - request->requestTimeoutTimer.cancel (); - - bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) && - nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46; - uint8_t replyKey[32], replyTag[32]; - RAND_bytes (replyKey, 32); // random session key - RAND_bytes (replyTag, isECIES ? 8 : 32); // random session tag - if (isECIES) - AddECIESx25519Key (replyKey, replyTag); - else - AddSessionKey (replyKey, replyTag); - auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, - request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); - request->outboundTunnel->SendTunnelDataMsg ( - { - i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - nextFloodfill->GetIdentHash (), 0, msg - } - }); - request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); - request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer, - shared_from_this (), std::placeholders::_1, dest)); - } - else - return false; - return true; - } - - void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto it = m_LeaseSetRequests.find (dest); - if (it != m_LeaseSetRequests.end ()) - { - bool done = false; - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) - { - auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); - if (floodfill) - { - // reset tunnels, because one them might fail - it->second->outboundTunnel = nullptr; - it->second->replyTunnel = nullptr; - done = !SendLeaseSetRequest (dest, floodfill, it->second); - } - else - done = true; - } - else - { - LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); - done = true; - } - - if (done) - { - auto requestComplete = it->second; - m_LeaseSetRequests.erase (it); - if (requestComplete) requestComplete->Complete (nullptr); - } - } - } - } - - void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - CleanupExpiredTags (); - CleanupRemoteLeaseSets (); - CleanupDestination (); - m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); - m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, - shared_from_this (), std::placeholders::_1)); - } - } - - void LeaseSetDestination::CleanupRemoteLeaseSets () - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::lock_guard lock(m_RemoteLeaseSetsMutex); - for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();) - { - if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired - { - LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); - it = m_RemoteLeaseSets.erase (it); - } - else - ++it; - } - } - - i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const - { - if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; - return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - } - - ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, - bool isPublic, const std::map * params): - LeaseSetDestination (service, isPublic, params), - m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), - m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), - m_DatagramDestination (nullptr), m_RefCounter (0), - m_ReadyChecker(service) - { - if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) - SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only - - // extract encryption type params for LS2 - std::set encryptionKeyTypes; - if (params) - { - auto it = params->find (I2CP_PARAM_LEASESET_ENCRYPTION_TYPE); - if (it != params->end ()) - { - // comma-separated values - std::vector values; - boost::split(values, it->second, boost::is_any_of(",")); - for (auto& it1: values) - { - try - { - encryptionKeyTypes.insert (std::stoi(it1)); - } - catch (std::exception& ex) - { - LogPrint (eLogInfo, "Destination: Unexpected crypto type ", it1, ". ", ex.what ()); - continue; - } - } - } - } - // if no param or valid crypto type use from identity - bool isSingleKey = false; - if (encryptionKeyTypes.empty ()) - { - isSingleKey = true; - encryptionKeyTypes.insert (GetIdentity ()->GetCryptoKeyType ()); - } - - for (auto& it: encryptionKeyTypes) - { - auto encryptionKey = new EncryptionKey (it); - if (IsPublic ()) - PersistTemporaryKeys (encryptionKey, isSingleKey); - else - encryptionKey->GenerateKeys (); - encryptionKey->CreateDecryptor (); - if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - { - m_ECIESx25519EncryptionKey.reset (encryptionKey); - if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) - SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2 - } - else - m_StandardEncryptionKey.reset (encryptionKey); - } - - if (IsPublic ()) - LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); - - try - { - if (params) - { - // extract streaming params - auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY); - if (it != params->end ()) - m_StreamingAckDelay = std::stoi(it->second); - it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS); - if (it != params->end ()) - m_IsStreamingAnswerPings = (it->second == "true"); - - if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) - { - // authentication for encrypted LeaseSet - auto authType = GetAuthType (); - if (authType > 0) - { - m_AuthKeys = std::make_shared >(); - if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH) - ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params); - else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) - ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params); - else - LogPrint (eLogError, "Destination: Unexpected auth type ", authType); - if (m_AuthKeys->size ()) - LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read"); - else - { - LogPrint (eLogError, "Destination: No auth keys read for auth type ", authType); - m_AuthKeys = nullptr; - } - } - } - } - } - catch (std::exception & ex) - { - LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); - } - } - - ClientDestination::~ClientDestination () - { - } - - void ClientDestination::Start () - { - LeaseSetDestination::Start (); - m_StreamingDestination = std::make_shared (GetSharedFromThis ()); // TODO: - m_StreamingDestination->Start (); - for (auto& it: m_StreamingDestinationsByPorts) - it.second->Start (); - } - - void ClientDestination::Stop () - { - LeaseSetDestination::Stop (); - m_ReadyChecker.cancel(); - m_StreamingDestination->Stop (); - //m_StreamingDestination->SetOwner (nullptr); - m_StreamingDestination = nullptr; - for (auto& it: m_StreamingDestinationsByPorts) - { - it.second->Stop (); - //it.second->SetOwner (nullptr); - } - m_StreamingDestinationsByPorts.clear (); - if (m_DatagramDestination) - { - delete m_DatagramDestination; - m_DatagramDestination = nullptr; - } - } - - void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) - { - uint32_t length = bufbe32toh (buf); - if(length > len - 4) - { - LogPrint(eLogError, "Destination: Data message length ", length, " exceeds buffer length ", len); - return; - } - buf += 4; - // we assume I2CP payload - uint16_t fromPort = bufbe16toh (buf + 4), // source - toPort = bufbe16toh (buf + 6); // destination - switch (buf[9]) - { - case PROTOCOL_TYPE_STREAMING: - { - // streaming protocol - auto dest = GetStreamingDestination (toPort); - if (dest) - dest->HandleDataMessagePayload (buf, length); - else - LogPrint (eLogError, "Destination: Missing streaming destination"); - } - break; - case PROTOCOL_TYPE_DATAGRAM: - // datagram protocol - if (m_DatagramDestination) - m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length); - else - LogPrint (eLogError, "Destination: Missing datagram destination"); - break; - case PROTOCOL_TYPE_RAW: - // raw datagram - if (m_DatagramDestination) - m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true); - else - LogPrint (eLogError, "Destination: Missing raw datagram destination"); - break; - default: - LogPrint (eLogError, "Destination: Data: Unexpected protocol ", buf[9]); - } - } - - void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) - { - if (!streamRequestComplete) - { - LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream"); - return; - } - auto leaseSet = FindLeaseSet (dest); - if (leaseSet) - streamRequestComplete(CreateStream (leaseSet, port)); - else - { - auto s = GetSharedFromThis (); - RequestDestination (dest, - [s, streamRequestComplete, port](std::shared_ptr ls) - { - if (ls) - streamRequestComplete(s->CreateStream (ls, port)); - else - streamRequestComplete (nullptr); - }); - } - } - - void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, int port) - { - if (!streamRequestComplete) - { - LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream"); - return; - } - auto s = GetSharedFromThis (); - RequestDestinationWithEncryptedLeaseSet (dest, - [s, streamRequestComplete, port](std::shared_ptr ls) - { - if (ls) - streamRequestComplete(s->CreateStream (ls, port)); - else - streamRequestComplete (nullptr); - }); - } - - std::shared_ptr ClientDestination::CreateStream (std::shared_ptr remote, int port) - { - if (m_StreamingDestination) - return m_StreamingDestination->CreateNewOutgoingStream (remote, port); - else - return nullptr; - } - - void ClientDestination::SendPing (const i2p::data::IdentHash& to) - { - if (m_StreamingDestination) - { - auto leaseSet = FindLeaseSet (to); - if (leaseSet) - m_StreamingDestination->SendPing (leaseSet); - else - { - auto s = m_StreamingDestination; - RequestDestination (to, - [s](std::shared_ptr ls) - { - if (ls) s->SendPing (ls); - }); - } - } - } - - void ClientDestination::SendPing (std::shared_ptr to) - { - auto s = m_StreamingDestination; - RequestDestinationWithEncryptedLeaseSet (to, - [s](std::shared_ptr ls) - { - if (ls) s->SendPing (ls); - }); - } - - std::shared_ptr ClientDestination::GetStreamingDestination (int port) const - { - if (port) - { - auto it = m_StreamingDestinationsByPorts.find (port); - if (it != m_StreamingDestinationsByPorts.end ()) - return it->second; - } - // if port is zero or not found, use default destination - return m_StreamingDestination; - } - - void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) - { - if (m_StreamingDestination) - m_StreamingDestination->SetAcceptor (acceptor); - } - - void ClientDestination::StopAcceptingStreams () - { - if (m_StreamingDestination) - m_StreamingDestination->ResetAcceptor (); - } - - bool ClientDestination::IsAcceptingStreams () const - { - if (m_StreamingDestination) - return m_StreamingDestination->IsAcceptorSet (); - return false; - } - - void ClientDestination::AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor) - { - if (m_StreamingDestination) - m_StreamingDestination->AcceptOnce (acceptor); - } - - std::shared_ptr ClientDestination::CreateStreamingDestination (int port, bool gzip) - { - auto dest = std::make_shared (GetSharedFromThis (), port, gzip); - if (port) - m_StreamingDestinationsByPorts[port] = dest; - else // update default - m_StreamingDestination = dest; - return dest; - } - - std::shared_ptr ClientDestination::RemoveStreamingDestination (int port) - { - if (port) - { - auto it = m_StreamingDestinationsByPorts.find (port); - if (it != m_StreamingDestinationsByPorts.end ()) - { - auto ret = it->second; - m_StreamingDestinationsByPorts.erase (it); - return ret; - } - } - return nullptr; - } - - i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip) - { - if (m_DatagramDestination == nullptr) - m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip); - return m_DatagramDestination; - } - - std::vector > ClientDestination::GetAllStreams () const - { - std::vector > ret; - if (m_StreamingDestination) - { - for (auto& it: m_StreamingDestination->GetStreams ()) - ret.push_back (it.second); - } - for (auto& it: m_StreamingDestinationsByPorts) - for (auto& it1: it.second->GetStreams ()) - ret.push_back (it1.second); - return ret; - } - - void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey) - { - if (!keys) return; - std::string ident = GetIdentHash().ToBase32(); - std::string path = i2p::fs::DataDirPath("destinations", - isSingleKey ? (ident + ".dat") : (ident + "." + std::to_string (keys->keyType) + ".dat")); - std::ifstream f(path, std::ifstream::binary); - - if (f) { - f.read ((char *)keys->pub, 256); - f.read ((char *)keys->priv, 256); - return; - } - - LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); - memset (keys->priv, 0, 256); - memset (keys->pub, 0, 256); - keys->GenerateKeys (); - // TODO:: persist crypto key type - std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); - if (f1) { - f1.write ((char *)keys->pub, 256); - f1.write ((char *)keys->priv, 256); - return; - } - LogPrint(eLogError, "Destinations: Can't save keys to ", path); - } - - void ClientDestination::CreateNewLeaseSet (const std::vector >& tunnels) - { - std::shared_ptr leaseSet; - if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) - { - if (m_StandardEncryptionKey) - { - leaseSet = std::make_shared (GetIdentity (), m_StandardEncryptionKey->pub, tunnels); - // sign - Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); - } - else - LogPrint (eLogError, "Destinations: Wrong encryption key type for LeaseSet type 1"); - } - else - { - // standard LS2 (type 3) first - i2p::data::LocalLeaseSet2::KeySections keySections; - if (m_ECIESx25519EncryptionKey) - keySections.push_back ({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub} ); - if (m_StandardEncryptionKey) - keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} ); - - bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; - auto ls2 = std::make_shared (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, - m_Keys, keySections, tunnels, IsPublic (), isPublishedEncrypted); - if (isPublishedEncrypted) // encrypt if type 5 - ls2 = std::make_shared (ls2, m_Keys, GetAuthType (), m_AuthKeys); - leaseSet = ls2; - } - SetLeaseSet (leaseSet); - } - - void ClientDestination::CleanupDestination () - { - if (m_DatagramDestination) m_DatagramDestination->CleanUp (); - } - - bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const - { - if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) - return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data); - if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) - return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data); - else - LogPrint (eLogError, "Destinations: Decryptor is not set"); - return false; - } - - bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const - { - return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey; - } - - const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const - { - if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr; - return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr; - } - - void ClientDestination::ReadAuthKey (const std::string& group, const std::map * params) - { - for (auto it: *params) - if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group)) - { - auto pos = it.second.find (':'); - if (pos != std::string::npos) - { - i2p::data::AuthPublicKey pubKey; - if (pubKey.FromBase64 (it.second.substr (pos+1))) - m_AuthKeys->push_back (pubKey); - else - LogPrint (eLogError, "Destination: Unexpected auth key ", it.second.substr (pos+1)); - } - } - } - - bool ClientDestination::DeleteStream (uint32_t recvStreamID) - { - if (m_StreamingDestination->DeleteStream (recvStreamID)) - return true; - for (auto it: m_StreamingDestinationsByPorts) - if (it.second->DeleteStream (recvStreamID)) - return true; - return false; - } - - RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): - RunnableService ("Destination"), - ClientDestination (GetIOService (), keys, isPublic, params) - { - } - - RunnableClientDestination::~RunnableClientDestination () - { - if (IsRunning ()) - Stop (); - } - - void RunnableClientDestination::Start () - { - if (!IsRunning ()) - { - ClientDestination::Start (); - StartIOService (); - } - } - - void RunnableClientDestination::Stop () - { - if (IsRunning ()) - { - ClientDestination::Stop (); - StopIOService (); - } - } - -} +namespace i2p { + namespace client { + LeaseSetDestination::LeaseSetDestination(boost::asio::io_service &service, + bool isPublic, const std::map *params) : + m_Service(service), m_IsPublic(isPublic), m_PublishReplyToken(0), + m_LastSubmissionTime(0), m_PublishConfirmationTimer(m_Service), + m_PublishVerificationTimer(m_Service), m_PublishDelayTimer(m_Service), m_CleanupTimer(m_Service), + m_LeaseSetType(DEFAULT_LEASESET_TYPE), m_AuthType(i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) { + int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH; + int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY; + int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; + int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY; + int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; + int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; + int numTags = DEFAULT_TAGS_TO_SEND; + std::shared_ptr > explicitPeers; + try { + if (params) { + auto it = params->find(I2CP_PARAM_INBOUND_TUNNEL_LENGTH); + if (it != params->end()) + inLen = std::stoi(it->second); + it = params->find(I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH); + if (it != params->end()) + outLen = std::stoi(it->second); + it = params->find(I2CP_PARAM_INBOUND_TUNNELS_QUANTITY); + if (it != params->end()) + inQty = std::stoi(it->second); + it = params->find(I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY); + if (it != params->end()) + outQty = std::stoi(it->second); + it = params->find(I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE); + if (it != params->end()) + inVar = std::stoi(it->second); + it = params->find(I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE); + if (it != params->end()) + outVar = std::stoi(it->second); + it = params->find(I2CP_PARAM_TAGS_TO_SEND); + if (it != params->end()) + numTags = std::stoi(it->second); + LogPrint(eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen, + " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags"); + it = params->find(I2CP_PARAM_RATCHET_INBOUND_TAGS); + if (it != params->end()) + SetNumRatchetInboundTags(std::stoi(it->second)); + it = params->find(I2CP_PARAM_EXPLICIT_PEERS); + if (it != params->end()) { + explicitPeers = std::make_shared >(); + std::stringstream ss(it->second); + std::string b64; + while (std::getline(ss, b64, ',')) { + i2p::data::IdentHash ident; + ident.FromBase64(b64); + explicitPeers->push_back(ident); + LogPrint(eLogInfo, "Destination: Added to explicit peers list: ", b64); + } + } + it = params->find(I2CP_PARAM_INBOUND_NICKNAME); + if (it != params->end()) m_Nickname = it->second; + else // try outbound + { + it = params->find(I2CP_PARAM_OUTBOUND_NICKNAME); + if (it != params->end()) m_Nickname = it->second; + // otherwise we set default nickname in Start when we know local address + } + it = params->find(I2CP_PARAM_DONT_PUBLISH_LEASESET); + if (it != params->end()) { + // oveeride isPublic + m_IsPublic = (it->second != "true"); + } + it = params->find(I2CP_PARAM_LEASESET_TYPE); + if (it != params->end()) + m_LeaseSetType = std::stoi(it->second); + if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { + // authentication for encrypted LeaseSet + it = params->find(I2CP_PARAM_LEASESET_AUTH_TYPE); + if (it != params->end()) { + auto authType = std::stoi(it->second); + if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && + authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) + m_AuthType = authType; + else + LogPrint(eLogError, "Destination: Unknown auth type ", authType); + } + } + it = params->find(I2CP_PARAM_LEASESET_PRIV_KEY); + if (it != params->end()) { + m_LeaseSetPrivKey.reset(new i2p::data::Tag<32>()); + if (m_LeaseSetPrivKey->FromBase64(it->second) != 32) { + LogPrint(eLogError, "Destination: Invalid value i2cp.leaseSetPrivKey ", it->second); + m_LeaseSetPrivKey.reset(nullptr); + } + } + } + } + catch (std::exception &ex) { + LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); + } + SetNumTags(numTags); + m_Pool = i2p::tunnel::tunnels.CreateTunnelPool(inLen, outLen, inQty, outQty, inVar, outVar); + if (explicitPeers) + m_Pool->SetExplicitPeers(explicitPeers); + if (params) { + auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY); + if (itr != params->end()) { + auto maxlatency = std::stoi(itr->second); + itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY); + if (itr != params->end()) { + auto minlatency = std::stoi(itr->second); + if (minlatency > 0 && maxlatency > 0) { + // set tunnel pool latency + LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minlatency, "ms, ", + maxlatency, "ms]"); + m_Pool->RequireLatency(minlatency, maxlatency); + } + } + } + } + } + + LeaseSetDestination::~LeaseSetDestination() { + if (m_Pool) + i2p::tunnel::tunnels.DeleteTunnelPool(m_Pool); + for (auto &it: m_LeaseSetRequests) + it.second->Complete(nullptr); + } + + void LeaseSetDestination::Start() { + if (m_Nickname.empty()) + m_Nickname = i2p::data::GetIdentHashAbbreviation(GetIdentHash()); // set default nickname + LoadTags(); + m_Pool->SetLocalDestination(shared_from_this()); + m_Pool->SetActive(true); + m_CleanupTimer.expires_from_now(boost::posix_time::minutes(DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.async_wait(std::bind(&LeaseSetDestination::HandleCleanupTimer, + shared_from_this(), std::placeholders::_1)); + } + + void LeaseSetDestination::Stop() { + m_CleanupTimer.cancel(); + m_PublishConfirmationTimer.cancel(); + m_PublishVerificationTimer.cancel(); + if (m_Pool) { + m_Pool->SetLocalDestination(nullptr); + i2p::tunnel::tunnels.StopTunnelPool(m_Pool); + } + SaveTags(); + CleanUp(); // GarlicDestination + } + + bool LeaseSetDestination::Reconfigure(std::map params) { + auto itr = params.find("i2cp.dontPublishLeaseSet"); + if (itr != params.end()) { + m_IsPublic = itr->second != "true"; + } + + int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency; + std::map intOpts = { + {I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen}, + {I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen}, + {I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant}, + {I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant}, + {I2CP_PARAM_TAGS_TO_SEND, numTags}, + {I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency}, + {I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency} + }; + + auto pool = GetTunnelPool(); + inLen = pool->GetNumInboundHops(); + outLen = pool->GetNumOutboundHops(); + inQuant = pool->GetNumInboundTunnels(); + outQuant = pool->GetNumOutboundTunnels(); + minLatency = 0; + maxLatency = 0; + + for (auto &opt: intOpts) { + itr = params.find(opt.first); + if (itr != params.end()) { + opt.second = std::stoi(itr->second); + } + } + pool->RequireLatency(minLatency, maxLatency); + return pool->Reconfigure(inLen, outLen, inQuant, outQuant); + } + + std::shared_ptr LeaseSetDestination::FindLeaseSet(const i2p::data::IdentHash &ident) { + std::shared_ptr remoteLS; + { + std::lock_guard lock(m_RemoteLeaseSetsMutex); + auto it = m_RemoteLeaseSets.find(ident); + if (it != m_RemoteLeaseSets.end()) + remoteLS = it->second; + } + + if (remoteLS) { + if (!remoteLS->IsExpired()) { + if (remoteLS->ExpiresSoon()) { + LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire"); + // update now before expiration for smooth handover + auto s = shared_from_this(); + RequestDestination(ident, [s, ident](std::shared_ptr ls) { + if (ls && !ls->IsExpired()) { + ls->PopulateLeases(); + { + std::lock_guard _lock(s->m_RemoteLeaseSetsMutex); + s->m_RemoteLeaseSets[ident] = ls; + } + } + }); + } + return remoteLS; + } else { + LogPrint(eLogWarning, "Destination: Remote LeaseSet expired"); + std::lock_guard lock(m_RemoteLeaseSetsMutex); + m_RemoteLeaseSets.erase(ident); + return nullptr; + } + } else { + auto ls = i2p::data::netdb.FindLeaseSet(ident); + if (ls && !ls->IsExpired()) { + ls->PopulateLeases(); // since we don't store them in netdb + std::lock_guard _lock(m_RemoteLeaseSetsMutex); + m_RemoteLeaseSets[ident] = ls; + return ls; + } + } + return nullptr; + } + + std::shared_ptr LeaseSetDestination::GetLeaseSet() { + if (!m_Pool) return nullptr; + if (!m_LeaseSet) + UpdateLeaseSet(); + auto ls = GetLeaseSetMt(); + return (ls && ls->GetInnerLeaseSet()) ? ls->GetInnerLeaseSet() : ls; // always non-encrypted + } + + std::shared_ptr LeaseSetDestination::GetLeaseSetMt() { + std::lock_guard l(m_LeaseSetMutex); + return m_LeaseSet; + } + + void LeaseSetDestination::SetLeaseSet(std::shared_ptr newLeaseSet) { + { + std::lock_guard l(m_LeaseSetMutex); + m_LeaseSet = newLeaseSet; + } + i2p::garlic::GarlicDestination::SetLeaseSetUpdated(); + if (m_IsPublic) { + auto s = shared_from_this(); + m_Service.post([s](void) { + s->m_PublishVerificationTimer.cancel(); + s->Publish(); + }); + } + } + + void LeaseSetDestination::UpdateLeaseSet() { + int numTunnels = m_Pool->GetNumInboundTunnels() + 2; // 2 backup tunnels + if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum + auto tunnels = m_Pool->GetInboundTunnels(numTunnels); + if (!tunnels.empty()) + CreateNewLeaseSet(tunnels); + else + LogPrint(eLogInfo, "Destination: No inbound tunnels for LeaseSet"); + } + + bool LeaseSetDestination::SubmitSessionKey(const uint8_t *key, const uint8_t *tag) { + struct { + uint8_t k[32], t[32]; + } data; + memcpy(data.k, key, 32); + memcpy(data.t, tag, 32); + auto s = shared_from_this(); + m_Service.post([s, data](void) { + s->AddSessionKey(data.k, data.t); + }); + return true; + } + + void LeaseSetDestination::SubmitECIESx25519Key(const uint8_t *key, uint64_t tag) { + struct { + uint8_t k[32]; + uint64_t t; + } data; + memcpy(data.k, key, 32); + data.t = tag; + auto s = shared_from_this(); + m_Service.post([s, data](void) { + s->AddECIESx25519Key(data.k, data.t); + }); + } + + void LeaseSetDestination::ProcessGarlicMessage(std::shared_ptr msg) { + m_Service.post(std::bind(&LeaseSetDestination::HandleGarlicMessage, shared_from_this(), msg)); + } + + void LeaseSetDestination::ProcessDeliveryStatusMessage(std::shared_ptr msg) { + uint32_t msgID = bufbe32toh(msg->GetPayload() + DELIVERY_STATUS_MSGID_OFFSET); + m_Service.post(std::bind(&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this(), msgID)); + } + + void LeaseSetDestination::HandleI2NPMessage(const uint8_t *buf, size_t len) { + I2NPMessageType typeID = (I2NPMessageType) (buf[I2NP_HEADER_TYPEID_OFFSET]); + uint32_t msgID = bufbe32toh(buf + I2NP_HEADER_MSGID_OFFSET); + LeaseSetDestination::HandleCloveI2NPMessage(typeID, buf + I2NP_HEADER_SIZE, + GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID); + } + + bool LeaseSetDestination::HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, + uint32_t msgID) { + switch (typeID) { + case eI2NPData: + HandleDataMessage(payload, len); + break; + case eI2NPDeliveryStatus: + // we assume tunnel tests non-encrypted + HandleDeliveryStatusMessage(bufbe32toh(payload + DELIVERY_STATUS_MSGID_OFFSET)); + break; + case eI2NPDatabaseStore: + HandleDatabaseStoreMessage(payload, len); + break; + case eI2NPDatabaseSearchReply: + HandleDatabaseSearchReplyMessage(payload, len); + break; + case eI2NPShortTunnelBuildReply: // might come as garlic encrypted + i2p::HandleI2NPMessage(CreateI2NPMessage(typeID, payload, len, msgID)); + break; + default: + LogPrint(eLogWarning, "Destination: Unexpected I2NP message type ", typeID); + return false; + } + return true; + } + + void LeaseSetDestination::HandleDatabaseStoreMessage(const uint8_t *buf, size_t len) { + uint32_t replyToken = bufbe32toh(buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); + size_t offset = DATABASE_STORE_HEADER_SIZE; + if (replyToken) { + LogPrint(eLogInfo, "Destination: Reply token is ignored for DatabaseStore"); + offset += 36; + } + i2p::data::IdentHash key(buf + DATABASE_STORE_KEY_OFFSET); + std::shared_ptr leaseSet; + switch (buf[DATABASE_STORE_TYPE_OFFSET]) { + case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1 + case i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2: // 3 + { + LogPrint(eLogDebug, "Destination: Remote LeaseSet"); + std::lock_guard lock(m_RemoteLeaseSetsMutex); + auto it = m_RemoteLeaseSets.find(key); + if (it != m_RemoteLeaseSets.end() && + it->second->GetStoreType() == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type + { + leaseSet = it->second; + if (leaseSet->IsNewer(buf + offset, len - offset)) { + leaseSet->Update(buf + offset, len - offset); + if (leaseSet->IsValid() && leaseSet->GetIdentHash() == key && !leaseSet->IsExpired()) + LogPrint(eLogDebug, "Destination: Remote LeaseSet updated"); + else { + LogPrint(eLogDebug, "Destination: Remote LeaseSet update failed"); + m_RemoteLeaseSets.erase(it); + leaseSet = nullptr; + } + } else + LogPrint(eLogDebug, "Destination: Remote LeaseSet is older. Not updated"); + } else { + // add or replace + if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) + leaseSet = std::make_shared(buf + offset, len - offset); // LeaseSet + else + leaseSet = std::make_shared(buf[DATABASE_STORE_TYPE_OFFSET], + buf + offset, len - offset, true, + GetPreferredCryptoType()); // LeaseSet2 + if (leaseSet->IsValid() && leaseSet->GetIdentHash() == key && !leaseSet->IsExpired()) { + if (leaseSet->GetIdentHash() != GetIdentHash()) { + LogPrint(eLogDebug, "Destination: New remote LeaseSet added"); + m_RemoteLeaseSets[key] = leaseSet; + } else + LogPrint(eLogDebug, "Destination: Own remote LeaseSet dropped"); + } else { + LogPrint(eLogError, "Destination: New remote LeaseSet failed"); + leaseSet = nullptr; + } + } + break; + } + case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 + { + auto it2 = m_LeaseSetRequests.find(key); + if (it2 != m_LeaseSetRequests.end() && it2->second->requestedBlindedKey) { + auto ls2 = std::make_shared(buf + offset, len - offset, + it2->second->requestedBlindedKey, + m_LeaseSetPrivKey + ? ((const uint8_t *) *m_LeaseSetPrivKey) + : nullptr, GetPreferredCryptoType()); + if (ls2->IsValid()) { + m_RemoteLeaseSets[ls2->GetIdentHash()] = ls2; // ident is not key + m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup + leaseSet = ls2; + } + } else + LogPrint(eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2"); + break; + } + default: + LogPrint(eLogError, "Destination: Unexpected client's DatabaseStore type ", + buf[DATABASE_STORE_TYPE_OFFSET], ", dropped"); + } + + auto it1 = m_LeaseSetRequests.find(key); + if (it1 != m_LeaseSetRequests.end()) { + it1->second->requestTimeoutTimer.cancel(); + if (it1->second) it1->second->Complete(leaseSet); + m_LeaseSetRequests.erase(it1); + } + } + + void LeaseSetDestination::HandleDatabaseSearchReplyMessage(const uint8_t *buf, size_t len) { + i2p::data::IdentHash key(buf); + int num = buf[32]; // num + LogPrint(eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64(), " num=", num); + auto it = m_LeaseSetRequests.find(key); + if (it != m_LeaseSetRequests.end()) { + auto request = it->second; + bool found = false; + if (request->excluded.size() < MAX_NUM_FLOODFILLS_PER_REQUEST) { + for (int i = 0; i < num; i++) { + i2p::data::IdentHash peerHash(buf + 33 + i * 32); + if (!request->excluded.count(peerHash) && !i2p::data::netdb.FindRouter(peerHash)) { + LogPrint(eLogInfo, "Destination: Found new floodfill, request it"); + i2p::data::netdb.RequestDestination(peerHash, nullptr, false); // through exploratory + } + } + + auto floodfill = i2p::data::netdb.GetClosestFloodfill(key, request->excluded); + if (floodfill) { + LogPrint(eLogInfo, "Destination: Requesting ", key.ToBase64(), " at ", + floodfill->GetIdentHash().ToBase64()); + if (SendLeaseSetRequest(key, floodfill, request)) + found = true; + } + } + if (!found) { + LogPrint(eLogInfo, "Destination: ", key.ToBase64(), " was not found on ", + MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills"); + request->Complete(nullptr); + m_LeaseSetRequests.erase(key); + } + } else + LogPrint(eLogWarning, "Destination: Request for ", key.ToBase64(), " not found"); + } + + void LeaseSetDestination::HandleDeliveryStatusMessage(uint32_t msgID) { + if (msgID == m_PublishReplyToken) { + LogPrint(eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32()); + m_ExcludedFloodfills.clear(); + m_PublishReplyToken = 0; + // schedule verification + m_PublishVerificationTimer.expires_from_now(boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); + m_PublishVerificationTimer.async_wait(std::bind(&LeaseSetDestination::HandlePublishVerificationTimer, + shared_from_this(), std::placeholders::_1)); + } else + i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage(msgID); + } + + void LeaseSetDestination::SetLeaseSetUpdated() { + UpdateLeaseSet(); + } + + void LeaseSetDestination::Publish() { + auto leaseSet = GetLeaseSetMt(); + if (!leaseSet || !m_Pool) { + LogPrint(eLogError, "Destination: Can't publish non-existing LeaseSet"); + return; + } + if (m_PublishReplyToken) { + LogPrint(eLogDebug, "Destination: Publishing LeaseSet is pending"); + return; + } + auto ts = i2p::util::GetSecondsSinceEpoch(); + if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL) { + LogPrint(eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, + " seconds"); + m_PublishDelayTimer.cancel(); + m_PublishDelayTimer.expires_from_now(boost::posix_time::seconds(PUBLISH_MIN_INTERVAL)); + m_PublishDelayTimer.async_wait(std::bind(&LeaseSetDestination::HandlePublishDelayTimer, + shared_from_this(), std::placeholders::_1)); + return; + } + if (!m_Pool->GetInboundTunnels().size() || !m_Pool->GetOutboundTunnels().size()) { + LogPrint(eLogError, "Destination: Can't publish LeaseSet. Destination is not ready"); + return; + } + auto floodfill = i2p::data::netdb.GetClosestFloodfill(leaseSet->GetIdentHash(), m_ExcludedFloodfills); + if (!floodfill) { + LogPrint(eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); + m_ExcludedFloodfills.clear(); + return; + } + auto outbound = m_Pool->GetNextOutboundTunnel(nullptr, floodfill->GetCompatibleTransports(false)); + auto inbound = m_Pool->GetNextInboundTunnel(nullptr, floodfill->GetCompatibleTransports(true)); + if (!outbound || !inbound) { + LogPrint(eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash().ToBase64(), + ". Trying another floodfill"); + m_ExcludedFloodfills.insert(floodfill->GetIdentHash()); + floodfill = i2p::data::netdb.GetClosestFloodfill(leaseSet->GetIdentHash(), m_ExcludedFloodfills); + if (floodfill) { + outbound = m_Pool->GetNextOutboundTunnel(nullptr, floodfill->GetCompatibleTransports(false)); + if (outbound) { + inbound = m_Pool->GetNextInboundTunnel(nullptr, floodfill->GetCompatibleTransports(true)); + if (!inbound) + LogPrint(eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); + } else + LogPrint(eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); + } else + LogPrint(eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); + if (!floodfill || !outbound || !inbound) { + m_ExcludedFloodfills.clear(); + return; + } + } + m_ExcludedFloodfills.insert(floodfill->GetIdentHash()); + LogPrint(eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash().ToBase32()); + RAND_bytes((uint8_t * ) & m_PublishReplyToken, 4); + auto msg = WrapMessageForRouter(floodfill, + i2p::CreateDatabaseStoreMsg(leaseSet, m_PublishReplyToken, inbound)); + m_PublishConfirmationTimer.expires_from_now(boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); + m_PublishConfirmationTimer.async_wait(std::bind(&LeaseSetDestination::HandlePublishConfirmationTimer, + shared_from_this(), std::placeholders::_1)); + outbound->SendTunnelDataMsg(floodfill->GetIdentHash(), 0, msg); + m_LastSubmissionTime = ts; + } + + void LeaseSetDestination::HandlePublishConfirmationTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + if (m_PublishReplyToken) { + m_PublishReplyToken = 0; + if (GetIdentity()->GetCryptoKeyType() == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) { + LogPrint(eLogWarning, "Destination: Publish confirmation was not received in ", + PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again"); + Publish(); + } else { + LogPrint(eLogWarning, "Destination: Publish confirmation was not received in ", + PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", + (int) GetIdentity()->GetCryptoKeyType()); + // Java floodfill never sends confirmation back for unknown crypto type + // assume it successive and try to verify + m_PublishVerificationTimer.expires_from_now( + boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); + m_PublishVerificationTimer.async_wait( + std::bind(&LeaseSetDestination::HandlePublishVerificationTimer, + shared_from_this(), std::placeholders::_1)); + + } + } + } + } + + void LeaseSetDestination::HandlePublishVerificationTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ls = GetLeaseSetMt(); + if (!ls) { + LogPrint(eLogWarning, "Destination: Couldn't verify LeaseSet for ", GetIdentHash().ToBase32()); + return; + } + auto s = shared_from_this(); + RequestLeaseSet(ls->GetStoreHash(), + [s, ls](std::shared_ptr leaseSet) { + if (leaseSet) { + if (*ls == *leaseSet) { + // we got latest LeasetSet + LogPrint(eLogDebug, "Destination: Published LeaseSet verified for ", + s->GetIdentHash().ToBase32()); + s->m_PublishVerificationTimer.expires_from_now( + boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); + s->m_PublishVerificationTimer.async_wait( + std::bind(&LeaseSetDestination::HandlePublishVerificationTimer, s, + std::placeholders::_1)); + return; + } else + LogPrint(eLogDebug, + "Destination: LeaseSet is different than just published for ", + s->GetIdentHash().ToBase32()); + } else + LogPrint(eLogWarning, "Destination: Couldn't find published LeaseSet for ", + s->GetIdentHash().ToBase32()); + // we have to publish again + s->Publish(); + }); + } + } + + void LeaseSetDestination::HandlePublishDelayTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) + Publish(); + } + + bool + LeaseSetDestination::RequestDestination(const i2p::data::IdentHash &dest, RequestComplete requestComplete) { + if (!m_Pool || !IsReady()) { + if (requestComplete) + m_Service.post([requestComplete](void) { requestComplete(nullptr); }); + return false; + } + m_Service.post(std::bind(&LeaseSetDestination::RequestLeaseSet, shared_from_this(), dest, requestComplete, + nullptr)); + return true; + } + + bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet( + std::shared_ptr dest, RequestComplete requestComplete) { + if (!dest || !m_Pool || !IsReady()) { + if (requestComplete) + m_Service.post([requestComplete](void) { requestComplete(nullptr); }); + return false; + } + auto storeHash = dest->GetStoreHash(); + auto leaseSet = FindLeaseSet(storeHash); + if (leaseSet) { + if (requestComplete) + m_Service.post([requestComplete, leaseSet](void) { requestComplete(leaseSet); }); + return true; + } + m_Service.post( + std::bind(&LeaseSetDestination::RequestLeaseSet, shared_from_this(), storeHash, requestComplete, + dest)); + return true; + } + + void LeaseSetDestination::CancelDestinationRequest(const i2p::data::IdentHash &dest, bool notify) { + auto s = shared_from_this(); + m_Service.post([dest, notify, s](void) { + auto it = s->m_LeaseSetRequests.find(dest); + if (it != s->m_LeaseSetRequests.end()) { + auto requestComplete = it->second; + s->m_LeaseSetRequests.erase(it); + if (notify && requestComplete) requestComplete->Complete(nullptr); + } + }); + } + + void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet( + std::shared_ptr dest, bool notify) { + if (dest) + CancelDestinationRequest(dest->GetStoreHash(), notify); + } + + void LeaseSetDestination::RequestLeaseSet(const i2p::data::IdentHash &dest, RequestComplete requestComplete, + std::shared_ptr requestedBlindedKey) { + std::set excluded; + auto floodfill = i2p::data::netdb.GetClosestFloodfill(dest, excluded); + if (floodfill) { + auto request = std::make_shared(m_Service); + request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2 + if (requestComplete) + request->requestComplete.push_back(requestComplete); + auto ts = i2p::util::GetSecondsSinceEpoch(); + auto ret = m_LeaseSetRequests.insert( + std::pair >(dest, request)); + if (ret.second) // inserted + { + request->requestTime = ts; + if (!SendLeaseSetRequest(dest, floodfill, request)) { + // request failed + m_LeaseSetRequests.erase(ret.first); + if (requestComplete) requestComplete(nullptr); + } + } else // duplicate + { + LogPrint(eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64(), " is pending already"); + if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) { + // something went wrong + m_LeaseSetRequests.erase(ret.first); + if (requestComplete) requestComplete(nullptr); + } else if (requestComplete) + ret.first->second->requestComplete.push_back(requestComplete); + } + } else { + LogPrint(eLogError, "Destination: Can't request LeaseSet, no floodfills found"); + if (requestComplete) requestComplete(nullptr); + } + } + + bool LeaseSetDestination::SendLeaseSetRequest(const i2p::data::IdentHash &dest, + std::shared_ptr nextFloodfill, + std::shared_ptr request) { + if (!request->replyTunnel || !request->replyTunnel->IsEstablished()) + request->replyTunnel = m_Pool->GetNextInboundTunnel(nullptr, + nextFloodfill->GetCompatibleTransports(true)); + if (!request->replyTunnel) + LogPrint(eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found"); + if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished()) + request->outboundTunnel = m_Pool->GetNextOutboundTunnel(nullptr, + nextFloodfill->GetCompatibleTransports(false)); + if (!request->outboundTunnel) + LogPrint(eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found"); + + if (request->replyTunnel && request->outboundTunnel) { + request->excluded.insert(nextFloodfill->GetIdentHash()); + request->requestTimeoutTimer.cancel(); + + bool isECIES = SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) && + nextFloodfill->GetVersion() >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46; + uint8_t replyKey[32], replyTag[32]; + RAND_bytes(replyKey, 32); // random session key + RAND_bytes(replyTag, isECIES ? 8 : 32); // random session tag + if (isECIES) + AddECIESx25519Key(replyKey, replyTag); + else + AddSessionKey(replyKey, replyTag); + auto msg = WrapMessageForRouter(nextFloodfill, CreateLeaseSetDatabaseLookupMsg(dest, + request->excluded, + request->replyTunnel, + replyKey, replyTag, + isECIES)); + request->outboundTunnel->SendTunnelDataMsg( + { + i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash(), 0, msg + } + }); + request->requestTimeoutTimer.expires_from_now(boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); + request->requestTimeoutTimer.async_wait(std::bind(&LeaseSetDestination::HandleRequestTimoutTimer, + shared_from_this(), std::placeholders::_1, dest)); + } else + return false; + return true; + } + + void LeaseSetDestination::HandleRequestTimoutTimer(const boost::system::error_code &ecode, + const i2p::data::IdentHash &dest) { + if (ecode != boost::asio::error::operation_aborted) { + auto it = m_LeaseSetRequests.find(dest); + if (it != m_LeaseSetRequests.end()) { + bool done = false; + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) { + auto floodfill = i2p::data::netdb.GetClosestFloodfill(dest, it->second->excluded); + if (floodfill) { + // reset tunnels, because one them might fail + it->second->outboundTunnel = nullptr; + it->second->replyTunnel = nullptr; + done = !SendLeaseSetRequest(dest, floodfill, it->second); + } else + done = true; + } else { + LogPrint(eLogWarning, "Destination: ", dest.ToBase64(), " was not found within ", + MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); + done = true; + } + + if (done) { + auto requestComplete = it->second; + m_LeaseSetRequests.erase(it); + if (requestComplete) requestComplete->Complete(nullptr); + } + } + } + } + + void LeaseSetDestination::HandleCleanupTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + CleanupExpiredTags(); + CleanupRemoteLeaseSets(); + CleanupDestination(); + m_CleanupTimer.expires_from_now(boost::posix_time::minutes(DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.async_wait(std::bind(&LeaseSetDestination::HandleCleanupTimer, + shared_from_this(), std::placeholders::_1)); + } + } + + void LeaseSetDestination::CleanupRemoteLeaseSets() { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + std::lock_guard lock(m_RemoteLeaseSetsMutex); + for (auto it = m_RemoteLeaseSets.begin(); it != m_RemoteLeaseSets.end();) { + if (it->second->IsEmpty() || ts > it->second->GetExpirationTime()) // leaseset expired + { + LogPrint(eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash().ToBase64(), + " expired"); + it = m_RemoteLeaseSets.erase(it); + } else + ++it; + } + } + + i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType() const { + if (SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) + return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; + return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + } + + ClientDestination::ClientDestination(boost::asio::io_service &service, const i2p::data::PrivateKeys &keys, + bool isPublic, const std::map *params) : + LeaseSetDestination(service, isPublic, params), + m_Keys(keys), m_StreamingAckDelay(DEFAULT_INITIAL_ACK_DELAY), + m_IsStreamingAnswerPings(DEFAULT_ANSWER_PINGS), + m_DatagramDestination(nullptr), m_RefCounter(0), + m_ReadyChecker(service) { + if (keys.IsOfflineSignature() && GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_LEASESET) + SetLeaseSetType( + i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only + + // extract encryption type params for LS2 + std::set encryptionKeyTypes; + if (params) { + auto it = params->find(I2CP_PARAM_LEASESET_ENCRYPTION_TYPE); + if (it != params->end()) { + // comma-separated values + std::vector values; + boost::split(values, it->second, boost::is_any_of(",")); + for (auto &it1: values) { + try { + encryptionKeyTypes.insert(std::stoi(it1)); + } + catch (std::exception &ex) { + LogPrint(eLogInfo, "Destination: Unexpected crypto type ", it1, ". ", ex.what()); + continue; + } + } + } + } + // if no param or valid crypto type use from identity + bool isSingleKey = false; + if (encryptionKeyTypes.empty()) { + isSingleKey = true; + encryptionKeyTypes.insert(GetIdentity()->GetCryptoKeyType()); + } + + for (auto &it: encryptionKeyTypes) { + auto encryptionKey = new EncryptionKey(it); + if (IsPublic()) + PersistTemporaryKeys(encryptionKey, isSingleKey); + else + encryptionKey->GenerateKeys(); + encryptionKey->CreateDecryptor(); + if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) { + m_ECIESx25519EncryptionKey.reset(encryptionKey); + if (GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_LEASESET) + SetLeaseSetType(i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2 + } else + m_StandardEncryptionKey.reset(encryptionKey); + } + + if (IsPublic()) + LogPrint(eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32(), " created"); + + try { + if (params) { + // extract streaming params + auto it = params->find(I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY); + if (it != params->end()) + m_StreamingAckDelay = std::stoi(it->second); + it = params->find(I2CP_PARAM_STREAMING_ANSWER_PINGS); + if (it != params->end()) + m_IsStreamingAnswerPings = (it->second == "true"); + + if (GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { + // authentication for encrypted LeaseSet + auto authType = GetAuthType(); + if (authType > 0) { + m_AuthKeys = std::make_shared >(); + if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH) + ReadAuthKey(I2CP_PARAM_LEASESET_CLIENT_DH, params); + else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) + ReadAuthKey(I2CP_PARAM_LEASESET_CLIENT_PSK, params); + else + LogPrint(eLogError, "Destination: Unexpected auth type ", authType); + if (m_AuthKeys->size()) + LogPrint(eLogInfo, "Destination: ", m_AuthKeys->size(), " auth keys read"); + else { + LogPrint(eLogError, "Destination: No auth keys read for auth type ", authType); + m_AuthKeys = nullptr; + } + } + } + } + } + catch (std::exception &ex) { + LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); + } + } + + ClientDestination::~ClientDestination() { + } + + void ClientDestination::Start() { + LeaseSetDestination::Start(); + m_StreamingDestination = std::make_shared(GetSharedFromThis()); // TODO: + m_StreamingDestination->Start(); + for (auto &it: m_StreamingDestinationsByPorts) + it.second->Start(); + } + + void ClientDestination::Stop() { + LeaseSetDestination::Stop(); + m_ReadyChecker.cancel(); + m_StreamingDestination->Stop(); + //m_StreamingDestination->SetOwner (nullptr); + m_StreamingDestination = nullptr; + for (auto &it: m_StreamingDestinationsByPorts) { + it.second->Stop(); + //it.second->SetOwner (nullptr); + } + m_StreamingDestinationsByPorts.clear(); + if (m_DatagramDestination) { + delete m_DatagramDestination; + m_DatagramDestination = nullptr; + } + } + + void ClientDestination::HandleDataMessage(const uint8_t *buf, size_t len) { + uint32_t length = bufbe32toh(buf); + if (length > len - 4) { + LogPrint(eLogError, "Destination: Data message length ", length, " exceeds buffer length ", len); + return; + } + buf += 4; + // we assume I2CP payload + uint16_t fromPort = bufbe16toh(buf + 4), // source + toPort = bufbe16toh(buf + 6); // destination + switch (buf[9]) { + case PROTOCOL_TYPE_STREAMING: { + // streaming protocol + auto dest = GetStreamingDestination(toPort); + if (dest) + dest->HandleDataMessagePayload(buf, length); + else + LogPrint(eLogError, "Destination: Missing streaming destination"); + } + break; + case PROTOCOL_TYPE_DATAGRAM: + // datagram protocol + if (m_DatagramDestination) + m_DatagramDestination->HandleDataMessagePayload(fromPort, toPort, buf, length); + else + LogPrint(eLogError, "Destination: Missing datagram destination"); + break; + case PROTOCOL_TYPE_RAW: + // raw datagram + if (m_DatagramDestination) + m_DatagramDestination->HandleDataMessagePayload(fromPort, toPort, buf, length, true); + else + LogPrint(eLogError, "Destination: Missing raw datagram destination"); + break; + default: + LogPrint(eLogError, "Destination: Data: Unexpected protocol ", buf[9]); + } + } + + void + ClientDestination::CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash &dest, + int port) { + if (!streamRequestComplete) { + LogPrint(eLogError, "Destination: Request callback is not specified in CreateStream"); + return; + } + auto leaseSet = FindLeaseSet(dest); + if (leaseSet) + streamRequestComplete(CreateStream(leaseSet, port)); + else { + auto s = GetSharedFromThis(); + RequestDestination(dest, + [s, streamRequestComplete, port](std::shared_ptr ls) { + if (ls) + streamRequestComplete(s->CreateStream(ls, port)); + else + streamRequestComplete(nullptr); + }); + } + } + + void ClientDestination::CreateStream(StreamRequestComplete streamRequestComplete, + std::shared_ptr dest, int port) { + if (!streamRequestComplete) { + LogPrint(eLogError, "Destination: Request callback is not specified in CreateStream"); + return; + } + auto s = GetSharedFromThis(); + RequestDestinationWithEncryptedLeaseSet(dest, + [s, streamRequestComplete, port]( + std::shared_ptr ls) { + if (ls) + streamRequestComplete(s->CreateStream(ls, port)); + else + streamRequestComplete(nullptr); + }); + } + + std::shared_ptr + ClientDestination::CreateStream(std::shared_ptr remote, int port) { + if (m_StreamingDestination) + return m_StreamingDestination->CreateNewOutgoingStream(remote, port); + else + return nullptr; + } + + void ClientDestination::SendPing(const i2p::data::IdentHash &to) { + if (m_StreamingDestination) { + auto leaseSet = FindLeaseSet(to); + if (leaseSet) + m_StreamingDestination->SendPing(leaseSet); + else { + auto s = m_StreamingDestination; + RequestDestination(to, + [s](std::shared_ptr ls) { + if (ls) s->SendPing(ls); + }); + } + } + } + + void ClientDestination::SendPing(std::shared_ptr to) { + auto s = m_StreamingDestination; + RequestDestinationWithEncryptedLeaseSet(to, + [s](std::shared_ptr ls) { + if (ls) s->SendPing(ls); + }); + } + + std::shared_ptr ClientDestination::GetStreamingDestination(int port) const { + if (port) { + auto it = m_StreamingDestinationsByPorts.find(port); + if (it != m_StreamingDestinationsByPorts.end()) + return it->second; + } + // if port is zero or not found, use default destination + return m_StreamingDestination; + } + + void ClientDestination::AcceptStreams(const i2p::stream::StreamingDestination::Acceptor &acceptor) { + if (m_StreamingDestination) + m_StreamingDestination->SetAcceptor(acceptor); + } + + void ClientDestination::StopAcceptingStreams() { + if (m_StreamingDestination) + m_StreamingDestination->ResetAcceptor(); + } + + bool ClientDestination::IsAcceptingStreams() const { + if (m_StreamingDestination) + return m_StreamingDestination->IsAcceptorSet(); + return false; + } + + void ClientDestination::AcceptOnce(const i2p::stream::StreamingDestination::Acceptor &acceptor) { + if (m_StreamingDestination) + m_StreamingDestination->AcceptOnce(acceptor); + } + + std::shared_ptr + ClientDestination::CreateStreamingDestination(int port, bool gzip) { + auto dest = std::make_shared(GetSharedFromThis(), port, gzip); + if (port) + m_StreamingDestinationsByPorts[port] = dest; + else // update default + m_StreamingDestination = dest; + return dest; + } + + std::shared_ptr ClientDestination::RemoveStreamingDestination(int port) { + if (port) { + auto it = m_StreamingDestinationsByPorts.find(port); + if (it != m_StreamingDestinationsByPorts.end()) { + auto ret = it->second; + m_StreamingDestinationsByPorts.erase(it); + return ret; + } + } + return nullptr; + } + + i2p::datagram::DatagramDestination *ClientDestination::CreateDatagramDestination(bool gzip) { + if (m_DatagramDestination == nullptr) + m_DatagramDestination = new i2p::datagram::DatagramDestination(GetSharedFromThis(), gzip); + return m_DatagramDestination; + } + + std::vector > ClientDestination::GetAllStreams() const { + std::vector > ret; + if (m_StreamingDestination) { + for (auto &it: m_StreamingDestination->GetStreams()) + ret.push_back(it.second); + } + for (auto &it: m_StreamingDestinationsByPorts) + for (auto &it1: it.second->GetStreams()) + ret.push_back(it1.second); + return ret; + } + + void ClientDestination::PersistTemporaryKeys(EncryptionKey *keys, bool isSingleKey) { + if (!keys) return; + std::string ident = GetIdentHash().ToBase32(); + std::string path = i2p::fs::DataDirPath("destinations", + isSingleKey ? (ident + ".dat") : (ident + "." + + std::to_string(keys->keyType) + + ".dat")); + std::ifstream f(path, std::ifstream::binary); + + if (f) { + f.read((char *) keys->pub, 256); + f.read((char *) keys->priv, 256); + return; + } + + LogPrint(eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); + memset(keys->priv, 0, 256); + memset(keys->pub, 0, 256); + keys->GenerateKeys(); + // TODO:: persist crypto key type + std::ofstream f1(path, std::ofstream::binary | std::ofstream::out); + if (f1) { + f1.write((char *) keys->pub, 256); + f1.write((char *) keys->priv, 256); + return; + } + LogPrint(eLogError, "Destinations: Can't save keys to ", path); + } + + void + ClientDestination::CreateNewLeaseSet(const std::vector > &tunnels) { + std::shared_ptr leaseSet; + if (GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_LEASESET) { + if (m_StandardEncryptionKey) { + leaseSet = std::make_shared(GetIdentity(), m_StandardEncryptionKey->pub, + tunnels); + // sign + Sign(leaseSet->GetBuffer(), leaseSet->GetBufferLen() - leaseSet->GetSignatureLen(), + leaseSet->GetSignature()); + } else + LogPrint(eLogError, "Destinations: Wrong encryption key type for LeaseSet type 1"); + } else { + // standard LS2 (type 3) first + i2p::data::LocalLeaseSet2::KeySections keySections; + if (m_ECIESx25519EncryptionKey) + keySections.push_back({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub}); + if (m_StandardEncryptionKey) + keySections.push_back({m_StandardEncryptionKey->keyType, + (uint16_t) m_StandardEncryptionKey->decryptor->GetPublicKeyLen(), + m_StandardEncryptionKey->pub}); + + bool isPublishedEncrypted = GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; + auto ls2 = std::make_shared(i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, + m_Keys, keySections, tunnels, IsPublic(), + isPublishedEncrypted); + if (isPublishedEncrypted) // encrypt if type 5 + ls2 = std::make_shared(ls2, m_Keys, GetAuthType(), m_AuthKeys); + leaseSet = ls2; + } + SetLeaseSet(leaseSet); + } + + void ClientDestination::CleanupDestination() { + if (m_DatagramDestination) m_DatagramDestination->CleanUp(); + } + + bool ClientDestination::Decrypt(const uint8_t *encrypted, uint8_t *data, + i2p::data::CryptoKeyType preferredCrypto) const { + if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) + return m_ECIESx25519EncryptionKey->decryptor->Decrypt(encrypted, data); + if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) + return m_StandardEncryptionKey->decryptor->Decrypt(encrypted, data); + else + LogPrint(eLogError, "Destinations: Decryptor is not set"); + return false; + } + + bool ClientDestination::SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const { + return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool) m_ECIESx25519EncryptionKey + : (bool) m_StandardEncryptionKey; + } + + const uint8_t *ClientDestination::GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const { + if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr; + return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr; + } + + void + ClientDestination::ReadAuthKey(const std::string &group, const std::map *params) { + for (auto it: *params) + if (it.first.length() >= group.length() && !it.first.compare(0, group.length(), group)) { + auto pos = it.second.find(':'); + if (pos != std::string::npos) { + i2p::data::AuthPublicKey pubKey; + if (pubKey.FromBase64(it.second.substr(pos + 1))) + m_AuthKeys->push_back(pubKey); + else + LogPrint(eLogError, "Destination: Unexpected auth key ", it.second.substr(pos + 1)); + } + } + } + + bool ClientDestination::DeleteStream(uint32_t recvStreamID) { + if (m_StreamingDestination->DeleteStream(recvStreamID)) + return true; + for (auto it: m_StreamingDestinationsByPorts) + if (it.second->DeleteStream(recvStreamID)) + return true; + return false; + } + + RunnableClientDestination::RunnableClientDestination(const i2p::data::PrivateKeys &keys, bool isPublic, + const std::map *params) : + RunnableService("Destination"), + ClientDestination(GetIOService(), keys, isPublic, params) { + } + + RunnableClientDestination::~RunnableClientDestination() { + if (IsRunning()) + Stop(); + } + + void RunnableClientDestination::Start() { + if (!IsRunning()) { + ClientDestination::Start(); + StartIOService(); + } + } + + void RunnableClientDestination::Stop() { + if (IsRunning()) { + ClientDestination::Stop(); + StopIOService(); + } + } + + } } diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 4b08ec51..8487bd3c 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -28,296 +28,378 @@ #include "Datagram.h" #include "util.h" -namespace i2p -{ -namespace client -{ - const uint8_t PROTOCOL_TYPE_STREAMING = 6; - const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; - const uint8_t PROTOCOL_TYPE_RAW = 18; - const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds - const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish - const int PUBLISH_MIN_INTERVAL = 20; // in seconds - const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically - const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds - const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds - const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes - const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; +namespace i2p { + namespace client { + const uint8_t PROTOCOL_TYPE_STREAMING = 6; + const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; + const uint8_t PROTOCOL_TYPE_RAW = 18; + const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds + const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish + const int PUBLISH_MIN_INTERVAL = 20; // in seconds + const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically + const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds + const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds + const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes + const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; - // I2CP - const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; - const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; - const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; - const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; - const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity"; - const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5; - const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity"; - const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5; - const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance"; - const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0; - const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance"; - const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0; - const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; - const int STREAM_REQUEST_TIMEOUT = 60; //in seconds - const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; - const int DEFAULT_TAGS_TO_SEND = 40; - 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_INBOUND_NICKNAME[] = "inbound.nickname"; - const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname"; - const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet"; - const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType"; - const int DEFAULT_LEASESET_TYPE = 3; - const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; - const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 - const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; - const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn - const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn + // I2CP + const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; + const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; + const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; + const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; + const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity"; + const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5; + const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity"; + const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5; + const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance"; + const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0; + const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance"; + const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0; + const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; + const int STREAM_REQUEST_TIMEOUT = 60; //in seconds + const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; + const int DEFAULT_TAGS_TO_SEND = 40; + 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_INBOUND_NICKNAME[] = "inbound.nickname"; + const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname"; + const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet"; + const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType"; + const int DEFAULT_LEASESET_TYPE = 3; + const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; + const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 + const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; + const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn + const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn - // latency - const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; - const int DEFAULT_MIN_TUNNEL_LATENCY = 0; - const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max"; - const int DEFAULT_MAX_TUNNEL_LATENCY = 0; + // latency + const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; + const int DEFAULT_MIN_TUNNEL_LATENCY = 0; + const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max"; + const int DEFAULT_MAX_TUNNEL_LATENCY = 0; - // streaming - const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay"; - const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds - const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; - const int DEFAULT_ANSWER_PINGS = true; + // streaming + const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay"; + const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds + const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; + const int DEFAULT_ANSWER_PINGS = true; - typedef std::function stream)> StreamRequestComplete; + typedef std::function stream)> StreamRequestComplete; - class LeaseSetDestination: public i2p::garlic::GarlicDestination, - public std::enable_shared_from_this - { - typedef std::function leaseSet)> RequestComplete; - // leaseSet = nullptr means not found - struct LeaseSetRequest - { - LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; - std::set excluded; - uint64_t requestTime; - boost::asio::deadline_timer requestTimeoutTimer; - std::list requestComplete; - std::shared_ptr outboundTunnel; - std::shared_ptr replyTunnel; - std::shared_ptr requestedBlindedKey; // for encrypted LeaseSet2 only + class LeaseSetDestination : public i2p::garlic::GarlicDestination, + public std::enable_shared_from_this { + typedef std::function leaseSet)> RequestComplete; - void Complete (std::shared_ptr ls) - { - for (auto& it: requestComplete) it (ls); - requestComplete.clear (); - } - }; + // leaseSet = nullptr means not found + struct LeaseSetRequest { + LeaseSetRequest(boost::asio::io_service &service) : requestTime(0), requestTimeoutTimer(service) {}; + std::set excluded; + uint64_t requestTime; + boost::asio::deadline_timer requestTimeoutTimer; + std::list requestComplete; + std::shared_ptr outboundTunnel; + std::shared_ptr replyTunnel; + std::shared_ptr requestedBlindedKey; // for encrypted LeaseSet2 only - public: + void Complete(std::shared_ptr ls) { + for (auto &it: requestComplete) it(ls); + requestComplete.clear(); + } + }; - LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map * params = nullptr); - ~LeaseSetDestination (); - const std::string& GetNickname () const { return m_Nickname; }; - boost::asio::io_service& GetService () { return m_Service; }; + public: - virtual void Start (); - virtual void Stop (); + LeaseSetDestination(boost::asio::io_service &service, bool isPublic, + const std::map *params = nullptr); - /** i2cp reconfigure */ - virtual bool Reconfigure(std::map i2cpOpts); + ~LeaseSetDestination(); - std::shared_ptr GetTunnelPool () { return m_Pool; }; - bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; - std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); - bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); - bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete = nullptr); - void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true); - void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify = true); + const std::string &GetNickname() const { return m_Nickname; }; - // implements GarlicDestination - std::shared_ptr GetLeaseSet (); - std::shared_ptr GetTunnelPool () const { return m_Pool; } + boost::asio::io_service &GetService() { return m_Service; }; - // override GarlicDestination - bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); - void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatusMessage (std::shared_ptr msg); - void SetLeaseSetUpdated (); + virtual void Start(); - bool IsPublic () const { return m_IsPublic; }; - void SetPublic (bool pub) { m_IsPublic = pub; }; + virtual void Stop(); - protected: + /** i2cp reconfigure */ + virtual bool Reconfigure(std::map i2cpOpts); - // 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); + std::shared_ptr GetTunnelPool() { return m_Pool; }; - void SetLeaseSet (std::shared_ptr newLeaseSet); - int GetLeaseSetType () const { return m_LeaseSetType; }; - 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 >& tunnels) = 0; + bool IsReady() const { + return m_LeaseSet && !m_LeaseSet->IsExpired() && m_Pool->GetOutboundTunnels().size() > 0; + }; - private: + std::shared_ptr FindLeaseSet(const i2p::data::IdentHash &ident); - void UpdateLeaseSet (); - std::shared_ptr 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); + bool RequestDestination(const i2p::data::IdentHash &dest, RequestComplete requestComplete = nullptr); - void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey = nullptr); - bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); - 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; + bool RequestDestinationWithEncryptedLeaseSet(std::shared_ptr dest, + RequestComplete requestComplete = nullptr); - private: + void CancelDestinationRequest(const i2p::data::IdentHash &dest, bool notify = true); - boost::asio::io_service& m_Service; - mutable std::mutex m_RemoteLeaseSetsMutex; - std::map > m_RemoteLeaseSets; - std::map > m_LeaseSetRequests; + void CancelDestinationRequestWithEncryptedLeaseSet(std::shared_ptr dest, + bool notify = true); - std::shared_ptr m_Pool; - std::mutex m_LeaseSetMutex; - std::shared_ptr m_LeaseSet; - bool m_IsPublic; - uint32_t m_PublishReplyToken; - uint64_t m_LastSubmissionTime; // in seconds - std::set m_ExcludedFloodfills; // for publishing + // implements GarlicDestination + std::shared_ptr GetLeaseSet(); - boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, - m_PublishDelayTimer, m_CleanupTimer; - std::string m_Nickname; - int m_LeaseSetType, m_AuthType; - std::unique_ptr > m_LeaseSetPrivKey; // non-null if presented + std::shared_ptr GetTunnelPool() const { return m_Pool; } - public: + // override GarlicDestination + bool SubmitSessionKey(const uint8_t *key, const uint8_t *tag); - // 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; }; - }; + void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag); - class ClientDestination: public LeaseSetDestination - { - struct EncryptionKey - { - uint8_t pub[256], priv[256]; - i2p::data::CryptoKeyType keyType; - std::shared_ptr decryptor; + void ProcessGarlicMessage(std::shared_ptr msg); - 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); }; - }; + void ProcessDeliveryStatusMessage(std::shared_ptr msg); - public: + void SetLeaseSetUpdated(); - ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, - bool isPublic, const std::map * params = nullptr); - ~ClientDestination (); + bool IsPublic() const { return m_IsPublic; }; - void Start (); - void Stop (); + void SetPublic(bool pub) { m_IsPublic = pub; }; - 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); }; + protected: - // ref counter - int Acquire () { return ++m_RefCounter; }; - int Release () { return --m_RefCounter; }; - int GetRefCounter () const { return m_RefCounter; }; + // implements GarlicDestination + void HandleI2NPMessage(const uint8_t *buf, size_t len); - // streaming - std::shared_ptr CreateStreamingDestination (int port, bool gzip = true); // additional - std::shared_ptr GetStreamingDestination (int port = 0) const; - std::shared_ptr 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 dest, int port = 0); - std::shared_ptr CreateStream (std::shared_ptr remote, int port = 0); - void SendPing (const i2p::data::IdentHash& to); - void SendPing (std::shared_ptr 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; } + bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID); - // datagram - i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; - i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true); + void SetLeaseSet(std::shared_ptr newLeaseSet); - // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; - std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; - const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; + int GetLeaseSetType() const { return m_LeaseSetType; }; - protected: + void SetLeaseSetType(int leaseSetType) { m_LeaseSetType = leaseSetType; }; - void CleanupDestination (); - // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len); - void CreateNewLeaseSet (const std::vector >& tunnels); + int GetAuthType() const { return m_AuthType; }; - 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 GetSharedFromThis () { - return std::static_pointer_cast(shared_from_this ()); - } - void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey); - void ReadAuthKey (const std::string& group, const std::map * params); + virtual void + CreateNewLeaseSet(const std::vector > &tunnels) = 0; - private: + private: - i2p::data::PrivateKeys m_Keys; - std::unique_ptr m_StandardEncryptionKey; - std::unique_ptr m_ECIESx25519EncryptionKey; + void UpdateLeaseSet(); - int m_StreamingAckDelay; - bool m_IsStreamingAnswerPings; - std::shared_ptr m_StreamingDestination; // default - std::map > m_StreamingDestinationsByPorts; - i2p::datagram::DatagramDestination * m_DatagramDestination; - int m_RefCounter; // how many clients(tunnels) use this destination + std::shared_ptr GetLeaseSetMt(); - boost::asio::deadline_timer m_ReadyChecker; + void Publish(); - std::shared_ptr > 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 - std::vector > GetAllStreams () const; - bool DeleteStream (uint32_t recvStreamID); - }; + void HandlePublishDelayTimer(const boost::system::error_code &ecode); - class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination - { - public: + void HandleDatabaseStoreMessage(const uint8_t *buf, size_t len); - RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); - ~RunnableClientDestination (); + void HandleDatabaseSearchReplyMessage(const uint8_t *buf, size_t len); - void Start (); - void Stop (); - }; + void HandleDeliveryStatusMessage(uint32_t msgID); -} + void RequestLeaseSet(const i2p::data::IdentHash &dest, RequestComplete requestComplete, + std::shared_ptr requestedBlindedKey = nullptr); + + bool SendLeaseSetRequest(const i2p::data::IdentHash &dest, + std::shared_ptr nextFloodfill, + std::shared_ptr request); + + 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 > m_RemoteLeaseSets; + std::map > m_LeaseSetRequests; + + std::shared_ptr m_Pool; + std::mutex m_LeaseSetMutex; + std::shared_ptr m_LeaseSet; + bool m_IsPublic; + uint32_t m_PublishReplyToken; + uint64_t m_LastSubmissionTime; // in seconds + std::set 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 > 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 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 *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 + CreateStreamingDestination(int port, bool gzip = true); // additional + std::shared_ptr GetStreamingDestination(int port = 0) const; + + std::shared_ptr 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 dest, int port = 0); + + std::shared_ptr + CreateStream(std::shared_ptr remote, int port = 0); + + void SendPing(const i2p::data::IdentHash &to); + + void SendPing(std::shared_ptr 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 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 > &tunnels); + + private: + + std::shared_ptr GetSharedFromThis() { + return std::static_pointer_cast(shared_from_this()); + } + + void PersistTemporaryKeys(EncryptionKey *keys, bool isSingleKey); + + void ReadAuthKey(const std::string &group, const std::map *params); + + private: + + i2p::data::PrivateKeys m_Keys; + std::unique_ptr m_StandardEncryptionKey; + std::unique_ptr m_ECIESx25519EncryptionKey; + + int m_StreamingAckDelay; + bool m_IsStreamingAnswerPings; + std::shared_ptr m_StreamingDestination; // default + std::map > 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 > m_AuthKeys; // we don't need them for I2CP + + public: + + // for HTTP only + std::vector > 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 *params = nullptr); + + ~RunnableClientDestination(); + + void Start(); + + void Stop(); + }; + + } } #endif diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index e240e925..4a44e70e 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -20,1191 +20,1160 @@ #include "Transports.h" #include "ECIESX25519AEADRatchetSession.h" -namespace i2p -{ -namespace garlic -{ +namespace i2p { + namespace garlic { - void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k) - { - // DH_INITIALIZE(rootKey, k) - uint8_t keydata[64]; - i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) - memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31] - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_SessionTagKeyData); - // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) - memcpy (m_SymmKeyCK, (const uint8_t *)m_SessionTagKeyData + 32, 32); - m_NextSymmKeyIndex = 0; - } + void RatchetTagSet::DHInitialize(const uint8_t *rootKey, const uint8_t *k) { + // DH_INITIALIZE(rootKey, k) + uint8_t keydata[64]; + i2p::crypto::HKDF(rootKey, k, 32, "KDFDHRatchetStep", + keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) + memcpy(m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31] + i2p::crypto::HKDF(keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_SessionTagKeyData); + // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) + memcpy(m_SymmKeyCK, (const uint8_t *) m_SessionTagKeyData + 32, 32); + m_NextSymmKeyIndex = 0; + } - void RatchetTagSet::NextSessionTagRatchet () - { - i2p::crypto::HKDF (m_SessionTagKeyData, nullptr, 0, "STInitialization", m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) - memcpy (m_SessTagConstant, (const uint8_t *)m_SessionTagKeyData + 32, 32); // SESSTAG_CONSTANT = keydata[32:63] - m_NextIndex = 0; - } + void RatchetTagSet::NextSessionTagRatchet() { + i2p::crypto::HKDF(m_SessionTagKeyData, nullptr, 0, "STInitialization", + m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) + memcpy(m_SessTagConstant, (const uint8_t *) m_SessionTagKeyData + 32, + 32); // SESSTAG_CONSTANT = keydata[32:63] + m_NextIndex = 0; + } - uint64_t RatchetTagSet::GetNextSessionTag () - { - m_NextIndex++; - if (m_NextIndex >= 65535) - { - LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty"); - return 0; - } - i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) - return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39] - } + uint64_t RatchetTagSet::GetNextSessionTag() { + m_NextIndex++; + if (m_NextIndex >= 65535) { + LogPrint(eLogError, "Garlic: Tagset ", GetTagSetID(), " is empty"); + return 0; + } + i2p::crypto::HKDF(m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", + m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) + return m_SessionTagKeyData.GetLL()[4]; // tag = keydata[32:39] + } - void RatchetTagSet::GetSymmKey (int index, uint8_t * key) - { - if (index >= m_NextSymmKeyIndex) - { - auto num = index + 1 - m_NextSymmKeyIndex; - if (!m_NextSymmKeyIndex) - { - i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64) - m_NextSymmKeyIndex = 1; - num--; - } - for (int i = 0; i < num; i++) - { - i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); - if (i < num - 1) - m_ItermediateSymmKeys.emplace (m_NextSymmKeyIndex + i, m_CurrentSymmKeyCK + 32); - } - m_NextSymmKeyIndex += num; - memcpy (key, m_CurrentSymmKeyCK + 32, 32); - } - else - { - auto it = m_ItermediateSymmKeys.find (index); - if (it != m_ItermediateSymmKeys.end ()) - { - memcpy (key, it->second, 32); - m_ItermediateSymmKeys.erase (it); - } - else - LogPrint (eLogError, "Garlic: Missing symmetric key for index ", index); - } - } + void RatchetTagSet::GetSymmKey(int index, uint8_t *key) { + if (index >= m_NextSymmKeyIndex) { + auto num = index + 1 - m_NextSymmKeyIndex; + if (!m_NextSymmKeyIndex) { + i2p::crypto::HKDF(m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", + m_CurrentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64) + m_NextSymmKeyIndex = 1; + num--; + } + for (int i = 0; i < num; i++) { + i2p::crypto::HKDF(m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); + if (i < num - 1) + m_ItermediateSymmKeys.emplace(m_NextSymmKeyIndex + i, m_CurrentSymmKeyCK + 32); + } + m_NextSymmKeyIndex += num; + memcpy(key, m_CurrentSymmKeyCK + 32, 32); + } else { + auto it = m_ItermediateSymmKeys.find(index); + if (it != m_ItermediateSymmKeys.end()) { + memcpy(key, it->second, 32); + m_ItermediateSymmKeys.erase(it); + } else + LogPrint(eLogError, "Garlic: Missing symmetric key for index ", index); + } + } - void RatchetTagSet::DeleteSymmKey (int index) - { - m_ItermediateSymmKeys.erase (index); - } + void RatchetTagSet::DeleteSymmKey(int index) { + m_ItermediateSymmKeys.erase(index); + } - void ReceiveRatchetTagSet::Expire () - { - if (!m_ExpirationTimestamp) - m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT; - } + void ReceiveRatchetTagSet::Expire() { + if (!m_ExpirationTimestamp) + m_ExpirationTimestamp = + i2p::util::GetSecondsSinceEpoch() + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT; + } - bool ReceiveRatchetTagSet::IsExpired (uint64_t ts) const - { - return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; - } + bool ReceiveRatchetTagSet::IsExpired(uint64_t ts) const { + return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; + } - bool ReceiveRatchetTagSet::IsIndexExpired (int index) const - { - return index < m_TrimBehindIndex; - } + bool ReceiveRatchetTagSet::IsIndexExpired(int index) const { + return index < m_TrimBehindIndex; + } - bool ReceiveRatchetTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) - { - auto session = GetSession (); - if (!session) return false; - return session->HandleNextMessage (buf, len, shared_from_this (), index); - } + bool ReceiveRatchetTagSet::HandleNextMessage(uint8_t *buf, size_t len, int index) { + auto session = GetSession(); + if (!session) return false; + return session->HandleNextMessage(buf, len, shared_from_this(), index); + } - SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key): - ReceiveRatchetTagSet (nullptr), m_Destination (destination) - { - memcpy (m_Key, key, 32); - Expire (); - } + SymmetricKeyTagSet::SymmetricKeyTagSet(GarlicDestination *destination, const uint8_t *key) : + ReceiveRatchetTagSet(nullptr), m_Destination(destination) { + memcpy(m_Key, key, 32); + Expire(); + } - bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) - { - if (len < 24) return false; - uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 - size_t offset = 8; // first 8 bytes is reply tag used as AD - len -= 16; // poly1305 - if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed"); - return false; - } - // we assume 1 I2NP block with delivery type local - if (offset + 3 > len) - { - LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len); - return false; - } - if (buf[offset] != eECIESx25519BlkGalicClove) - { - LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]); - return false; - } - offset++; - auto size = bufbe16toh (buf + offset); - offset += 2; - if (offset + size > len) - { - LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size); - return false; - } - if (m_Destination) - m_Destination->HandleECIESx25519GarlicClove (buf + offset, size); - return true; - } + bool SymmetricKeyTagSet::HandleNextMessage(uint8_t *buf, size_t len, int index) { + if (len < 24) return false; + uint8_t nonce[12]; + memset(nonce, 0, 12); // n = 0 + size_t offset = 8; // first 8 bytes is reply tag used as AD + len -= 16; // poly1305 + if (!i2p::crypto::AEADChaCha20Poly1305(buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, + len - offset, false)) // decrypt + { + LogPrint(eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed"); + return false; + } + // we assume 1 I2NP block with delivery type local + if (offset + 3 > len) { + LogPrint(eLogWarning, "Garlic: Symmetric key tagset is too short ", len); + return false; + } + if (buf[offset] != eECIESx25519BlkGalicClove) { + LogPrint(eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int) buf[offset]); + return false; + } + offset++; + auto size = bufbe16toh(buf + offset); + offset += 2; + if (offset + size > len) { + LogPrint(eLogWarning, "Garlic: Symmetric key tagset block is too long ", size); + return false; + } + if (m_Destination) + m_Destination->HandleECIESx25519GarlicClove(buf + offset, size); + return true; + } - ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS): - GarlicRoutingSession (owner, true) - { - if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate); - RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0; - } + ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession(GarlicDestination *owner, bool attachLeaseSetNS) : + GarlicRoutingSession(owner, true) { + if (!attachLeaseSetNS) SetLeaseSetUpdateStatus(eLeaseSetUpToDate); + RAND_bytes(m_PaddingSizes, 32); + m_NextPaddingSize = 0; + } - ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () - { - } + ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession() { + } - void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } + void ECIESX25519AEADRatchetSession::CreateNonce(uint64_t seqn, uint8_t *nonce) { + memset(nonce, 0, 4); + htole64buf(nonce + 4, seqn); + } - bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) - { - bool ineligible = false; - while (!ineligible) - { - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - ineligible = m_EphemeralKeys->IsElligatorIneligible (); - if (!ineligible) // we haven't tried it yet - { - if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf)) - return true; // success - // otherwise return back - m_EphemeralKeys->SetElligatorIneligible (); - i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); - } - else - i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); - } - // we still didn't find elligator eligible pair - for (int i = 0; i < 25; i++) - { - // create new - m_EphemeralKeys = std::make_shared(); - m_EphemeralKeys->GenerateKeys (); - if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf)) - return true; // success - else - { - // let NTCP2 use it - m_EphemeralKeys->SetElligatorIneligible (); - i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); - } - } - LogPrint (eLogError, "Garlic: Can't generate elligator eligible x25519 keys"); - return false; - } + bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode(uint8_t *buf) { + bool ineligible = false; + while (!ineligible) { + m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair(); + ineligible = m_EphemeralKeys->IsElligatorIneligible(); + if (!ineligible) // we haven't tried it yet + { + if (i2p::crypto::GetElligator()->Encode(m_EphemeralKeys->GetPublicKey(), buf)) + return true; // success + // otherwise return back + m_EphemeralKeys->SetElligatorIneligible(); + i2p::transport::transports.ReuseX25519KeysPair(m_EphemeralKeys); + } else + i2p::transport::transports.ReuseX25519KeysPair(m_EphemeralKeys); + } + // we still didn't find elligator eligible pair + for (int i = 0; i < 25; i++) { + // create new + m_EphemeralKeys = std::make_shared(); + m_EphemeralKeys->GenerateKeys(); + if (i2p::crypto::GetElligator()->Encode(m_EphemeralKeys->GetPublicKey(), buf)) + return true; // success + else { + // let NTCP2 use it + m_EphemeralKeys->SetElligatorIneligible(); + i2p::transport::transports.ReuseX25519KeysPair(m_EphemeralKeys); + } + } + LogPrint(eLogError, "Garlic: Can't generate elligator eligible x25519 keys"); + return false; + } - void ECIESX25519AEADRatchetSession::InitNewSessionTagset (std::shared_ptr tagsetNsr) const - { - uint8_t tagsetKey[32]; - i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) - // Session Tag Ratchet - tagsetNsr->DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) - tagsetNsr->NextSessionTagRatchet (); - } + void ECIESX25519AEADRatchetSession::InitNewSessionTagset(std::shared_ptr tagsetNsr) const { + uint8_t tagsetKey[32]; + i2p::crypto::HKDF(m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, + 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) + // Session Tag Ratchet + tagsetNsr->DHInitialize(m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) + tagsetNsr->NextSessionTagRatchet(); + } - bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) - { - if (!GetOwner ()) return false; - // we are Bob - // KDF1 - i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk + bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession(const uint8_t *buf, size_t len) { + if (!GetOwner()) return false; + // we are Bob + // KDF1 + i2p::crypto::InitNoiseIKState(GetNoiseState(), GetOwner()->GetEncryptionPublicKey( + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk - if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) - { - LogPrint (eLogError, "Garlic: Can't decode elligator"); - return false; - } - buf += 32; len -= 32; - MixHash (m_Aepk, 32); // h = SHA256(h || aepk) + if (!i2p::crypto::GetElligator()->Decode(buf, m_Aepk)) { + LogPrint(eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; + len -= 32; + MixHash(m_Aepk, 32); // h = SHA256(h || aepk) - uint8_t sharedSecret[32]; - if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); - return false; - } - MixKey (sharedSecret); + uint8_t sharedSecret[32]; + if (!GetOwner()->Decrypt(m_Aepk, sharedSecret, + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Alice ephemeral key"); + return false; + } + MixKey(sharedSecret); - // decrypt flags/static - uint8_t nonce[12], fs[32]; - CreateNonce (0, nonce); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed "); - return false; - } - MixHash (buf, 48); // h = SHA256(h || ciphertext) - buf += 48; len -= 48; // 32 data + 16 poly + // decrypt flags/static + uint8_t nonce[12], fs[32]; + CreateNonce(0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305(buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt + { + LogPrint(eLogWarning, "Garlic: Flags/static section AEAD verification failed "); + return false; + } + MixHash(buf, 48); // h = SHA256(h || ciphertext) + buf += 48; + len -= 48; // 32 data + 16 poly - // KDF2 for payload - bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); - if (isStatic) - { - // static key, fs is apk - memcpy (m_RemoteStaticKey, fs, 32); - if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); - return false; - } - MixKey (sharedSecret); - } - else // all zeros flags - CreateNonce (1, nonce); + // KDF2 for payload + bool isStatic = !i2p::data::Tag<32>(fs).IsZero(); + if (isStatic) { + // static key, fs is apk + memcpy(m_RemoteStaticKey, fs, 32); + if (!GetOwner()->Decrypt(fs, sharedSecret, + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Alice static key"); + return false; + } + MixKey(sharedSecret); + } else // all zeros flags + CreateNonce(1, nonce); - // decrypt payload - std::vector payload (len - 16); // we must save original ciphertext - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); - return false; - } + // decrypt payload + std::vector payload(len - 16); // we must save original ciphertext + if (!i2p::crypto::AEADChaCha20Poly1305(buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data(), len - 16, + false)) // decrypt + { + LogPrint(eLogWarning, "Garlic: Payload section AEAD verification failed"); + return false; + } - m_State = eSessionStateNewSessionReceived; - if (isStatic) - { - MixHash (buf, len); // h = SHA256(h || ciphertext) - GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); - } - HandlePayload (payload.data (), len - 16, nullptr, 0); + m_State = eSessionStateNewSessionReceived; + if (isStatic) { + MixHash(buf, len); // h = SHA256(h || ciphertext) + GetOwner()->AddECIESx25519Session(m_RemoteStaticKey, shared_from_this()); + } + HandlePayload(payload.data(), len - 16, nullptr, 0); - return true; - } + return true; + } - void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index) - { - size_t offset = 0; - while (offset < len) - { - uint8_t blk = buf[offset]; - offset++; - auto size = bufbe16toh (buf + offset); - offset += 2; - LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size); - if (size > len) - { - LogPrint (eLogError, "Garlic: Unexpected block length ", size); - break; - } - switch (blk) - { - case eECIESx25519BlkGalicClove: - if (GetOwner ()) - GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); - break; - case eECIESx25519BlkNextKey: - LogPrint (eLogDebug, "Garlic: Next key"); - if (receiveTagset) - HandleNextKey (buf + offset, size, receiveTagset); - else - LogPrint (eLogError, "Garlic: Unexpected next key block"); - break; - case eECIESx25519BlkAck: - { - LogPrint (eLogDebug, "Garlic: Ack"); - int numAcks = size >> 2; // /4 - auto offset1 = offset; - for (auto i = 0; i < numAcks; i++) - { - offset1 += 2; // tagsetid - MessageConfirmed (bufbe16toh (buf + offset1)); offset1 += 2; // N - } - break; - } - case eECIESx25519BlkAckRequest: - { - LogPrint (eLogDebug, "Garlic: Ack request"); - m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index}); - break; - } - case eECIESx25519BlkTermination: - LogPrint (eLogDebug, "Garlic: Termination"); - if (GetOwner ()) - GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); - if (receiveTagset) receiveTagset->Expire (); - break; - case eECIESx25519BlkDateTime: - LogPrint (eLogDebug, "Garlic: Datetime"); - break; - case eECIESx25519BlkOptions: - LogPrint (eLogDebug, "Garlic: Options"); - break; - case eECIESx25519BlkPadding: - LogPrint (eLogDebug, "Garlic: Padding"); - break; - default: - LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk); - } - offset += size; - } - } + void ECIESX25519AEADRatchetSession::HandlePayload(const uint8_t *buf, size_t len, + const std::shared_ptr &receiveTagset, + int index) { + size_t offset = 0; + while (offset < len) { + uint8_t blk = buf[offset]; + offset++; + auto size = bufbe16toh(buf + offset); + offset += 2; + LogPrint(eLogDebug, "Garlic: Block type ", (int) blk, " of size ", size); + if (size > len) { + LogPrint(eLogError, "Garlic: Unexpected block length ", size); + break; + } + switch (blk) { + case eECIESx25519BlkGalicClove: + if (GetOwner()) + GetOwner()->HandleECIESx25519GarlicClove(buf + offset, size); + break; + case eECIESx25519BlkNextKey: + LogPrint(eLogDebug, "Garlic: Next key"); + if (receiveTagset) + HandleNextKey(buf + offset, size, receiveTagset); + else + LogPrint(eLogError, "Garlic: Unexpected next key block"); + break; + case eECIESx25519BlkAck: { + LogPrint(eLogDebug, "Garlic: Ack"); + int numAcks = size >> 2; // /4 + auto offset1 = offset; + for (auto i = 0; i < numAcks; i++) { + offset1 += 2; // tagsetid + MessageConfirmed(bufbe16toh(buf + offset1)); + offset1 += 2; // N + } + break; + } + case eECIESx25519BlkAckRequest: { + LogPrint(eLogDebug, "Garlic: Ack request"); + m_AckRequests.push_back({receiveTagset->GetTagSetID(), index}); + break; + } + case eECIESx25519BlkTermination: + LogPrint(eLogDebug, "Garlic: Termination"); + if (GetOwner()) + GetOwner()->RemoveECIESx25519Session(m_RemoteStaticKey); + if (receiveTagset) receiveTagset->Expire(); + break; + case eECIESx25519BlkDateTime: + LogPrint(eLogDebug, "Garlic: Datetime"); + break; + case eECIESx25519BlkOptions: + LogPrint(eLogDebug, "Garlic: Options"); + break; + case eECIESx25519BlkPadding: + LogPrint(eLogDebug, "Garlic: Padding"); + break; + default: + LogPrint(eLogWarning, "Garlic: Unknown block type ", (int) blk); + } + offset += size; + } + } - void ECIESX25519AEADRatchetSession::HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset) - { - uint8_t flag = buf[0]; buf++; // flag - if (flag & ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG) - { - if (!m_SendForwardKey || !m_NextSendRatchet) return; - uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID - if (((!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) && keyID == m_NextSendRatchet->keyID) || - (m_NextSendRatchet->newKey && keyID == m_NextSendRatchet->keyID -1)) - { - if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) - memcpy (m_NextSendRatchet->remote, buf, 32); - uint8_t sharedSecret[32], tagsetKey[32]; - m_NextSendRatchet->key->Agree (m_NextSendRatchet->remote, sharedSecret); - i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) - auto newTagset = std::make_shared (); - newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID); - newTagset->DHInitialize (m_SendTagset->GetNextRootKey (), tagsetKey); - newTagset->NextSessionTagRatchet (); - m_SendTagset = newTagset; - m_SendForwardKey = false; - LogPrint (eLogDebug, "Garlic: Next send tagset ", newTagset->GetTagSetID (), " created"); - } - else - LogPrint (eLogDebug, "Garlic: Unexpected next key ", keyID); - } - else - { - uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID - bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; - m_SendReverseKey = true; - if (!m_NextReceiveRatchet) - m_NextReceiveRatchet.reset (new DHRatchet ()); - else - { - if (keyID == m_NextReceiveRatchet->keyID && newKey == m_NextReceiveRatchet->newKey) - { - LogPrint (eLogDebug, "Garlic: Duplicate ", newKey ? "new" : "old", " key ", keyID, " received"); - return; - } - m_NextReceiveRatchet->keyID = keyID; - } - int tagsetID = 2*keyID; - if (newKey) - { - m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair (); - m_NextReceiveRatchet->newKey = true; - tagsetID++; - } - else - m_NextReceiveRatchet->newKey = false; - if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) - memcpy (m_NextReceiveRatchet->remote, buf, 32); + void ECIESX25519AEADRatchetSession::HandleNextKey(const uint8_t *buf, size_t len, + const std::shared_ptr &receiveTagset) { + uint8_t flag = buf[0]; + buf++; // flag + if (flag & ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG) { + if (!m_SendForwardKey || !m_NextSendRatchet) return; + uint16_t keyID = bufbe16toh(buf); + buf += 2; // keyID + if (((!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) && keyID == m_NextSendRatchet->keyID) || + (m_NextSendRatchet->newKey && keyID == m_NextSendRatchet->keyID - 1)) { + if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) + memcpy(m_NextSendRatchet->remote, buf, 32); + uint8_t sharedSecret[32], tagsetKey[32]; + m_NextSendRatchet->key->Agree(m_NextSendRatchet->remote, sharedSecret); + i2p::crypto::HKDF(sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, + 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) + auto newTagset = std::make_shared(); + newTagset->SetTagSetID(1 + m_NextSendRatchet->keyID + keyID); + newTagset->DHInitialize(m_SendTagset->GetNextRootKey(), tagsetKey); + newTagset->NextSessionTagRatchet(); + m_SendTagset = newTagset; + m_SendForwardKey = false; + LogPrint(eLogDebug, "Garlic: Next send tagset ", newTagset->GetTagSetID(), " created"); + } else + LogPrint(eLogDebug, "Garlic: Unexpected next key ", keyID); + } else { + uint16_t keyID = bufbe16toh(buf); + buf += 2; // keyID + bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; + m_SendReverseKey = true; + if (!m_NextReceiveRatchet) + m_NextReceiveRatchet.reset(new DHRatchet()); + else { + if (keyID == m_NextReceiveRatchet->keyID && newKey == m_NextReceiveRatchet->newKey) { + LogPrint(eLogDebug, "Garlic: Duplicate ", newKey ? "new" : "old", " key ", keyID, " received"); + return; + } + m_NextReceiveRatchet->keyID = keyID; + } + int tagsetID = 2 * keyID; + if (newKey) { + m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair(); + m_NextReceiveRatchet->newKey = true; + tagsetID++; + } else + m_NextReceiveRatchet->newKey = false; + if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) + memcpy(m_NextReceiveRatchet->remote, buf, 32); - uint8_t sharedSecret[32], tagsetKey[32]; - m_NextReceiveRatchet->key->Agree (m_NextReceiveRatchet->remote, sharedSecret); - i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) - auto newTagset = std::make_shared(shared_from_this ()); - newTagset->SetTagSetID (tagsetID); - newTagset->DHInitialize (receiveTagset->GetNextRootKey (), tagsetKey); - newTagset->NextSessionTagRatchet (); - GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? - GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS); - receiveTagset->Expire (); - LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created"); - } - } + uint8_t sharedSecret[32], tagsetKey[32]; + m_NextReceiveRatchet->key->Agree(m_NextReceiveRatchet->remote, sharedSecret); + i2p::crypto::HKDF(sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, + 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) + auto newTagset = std::make_shared(shared_from_this()); + newTagset->SetTagSetID(tagsetID); + newTagset->DHInitialize(receiveTagset->GetNextRootKey(), tagsetKey); + newTagset->NextSessionTagRatchet(); + GenerateMoreReceiveTags(newTagset, (GetOwner() && GetOwner()->GetNumRatchetInboundTags() > 0) ? + GetOwner()->GetNumRatchetInboundTags() + : ECIESX25519_MAX_NUM_GENERATED_TAGS); + receiveTagset->Expire(); + LogPrint(eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created"); + } + } - void ECIESX25519AEADRatchetSession::NewNextSendRatchet () - { - if (m_NextSendRatchet) - { - if (!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) - { - m_NextSendRatchet->keyID++; - m_NextSendRatchet->newKey = true; - } - else - m_NextSendRatchet->newKey = false; - } - else - m_NextSendRatchet.reset (new DHRatchet ()); - if (m_NextSendRatchet->newKey) - m_NextSendRatchet->key = i2p::transport::transports.GetNextX25519KeysPair (); + void ECIESX25519AEADRatchetSession::NewNextSendRatchet() { + if (m_NextSendRatchet) { + if (!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) { + m_NextSendRatchet->keyID++; + m_NextSendRatchet->newKey = true; + } else + m_NextSendRatchet->newKey = false; + } else + m_NextSendRatchet.reset(new DHRatchet()); + if (m_NextSendRatchet->newKey) + m_NextSendRatchet->key = i2p::transport::transports.GetNextX25519KeysPair(); - m_SendForwardKey = true; - LogPrint (eLogDebug, "Garlic: New send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); - } + m_SendForwardKey = true; + LogPrint(eLogDebug, "Garlic: New send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", + m_NextSendRatchet->keyID, " created"); + } - bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic) - { - // we are Alice, bpk is m_RemoteStaticKey - size_t offset = 0; - if (!GenerateEphemeralKeysAndEncode (out + offset)) - { - LogPrint (eLogError, "Garlic: Can't encode elligator"); - return false; - } - offset += 32; + bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, + size_t outLen, bool isStatic) { + // we are Alice, bpk is m_RemoteStaticKey + size_t offset = 0; + if (!GenerateEphemeralKeysAndEncode(out + offset)) { + LogPrint(eLogError, "Garlic: Can't encode elligator"); + return false; + } + offset += 32; - // KDF1 - i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk - MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) - uint8_t sharedSecret[32]; - if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); - return false; - } - MixKey (sharedSecret); - // encrypt flags/static key section - uint8_t nonce[12]; - CreateNonce (0, nonce); - const uint8_t * fs; - if (isStatic) - fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); - else - { - memset (out + offset, 0, 32); // all zeros flags section - fs = out + offset; - } - if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed "); - return false; - } + // KDF1 + i2p::crypto::InitNoiseIKState(GetNoiseState(), m_RemoteStaticKey); // bpk + MixHash(m_EphemeralKeys->GetPublicKey(), 32); // h = SHA256(h || aepk) + uint8_t sharedSecret[32]; + if (!m_EphemeralKeys->Agree(m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Bob static key"); + return false; + } + MixKey(sharedSecret); + // encrypt flags/static key section + uint8_t nonce[12]; + CreateNonce(0, nonce); + const uint8_t *fs; + if (isStatic) + fs = GetOwner()->GetEncryptionPublicKey(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); + else { + memset(out + offset, 0, 32); // all zeros flags section + fs = out + offset; + } + if (!i2p::crypto::AEADChaCha20Poly1305(fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, + true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: Flags/static section AEAD encryption failed "); + return false; + } - MixHash (out + offset, 48); // h = SHA256(h || ciphertext) - offset += 48; - // KDF2 - if (isStatic) - { - GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) - MixKey (sharedSecret); - } - else - CreateNonce (1, nonce); - // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); - return false; - } + MixHash(out + offset, 48); // h = SHA256(h || ciphertext) + offset += 48; + // KDF2 + if (isStatic) { + GetOwner()->Decrypt(m_RemoteStaticKey, sharedSecret, + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) + MixKey(sharedSecret); + } else + CreateNonce(1, nonce); + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, + true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } - m_State = eSessionStateNewSessionSent; - if (isStatic) - { - MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) - if (GetOwner ()) - { - auto tagsetNsr = std::make_shared(shared_from_this (), true); - InitNewSessionTagset (tagsetNsr); - tagsetNsr->Expire (); // let non-replied session expire - GenerateMoreReceiveTags (tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS); - } - } - return true; - } + m_State = eSessionStateNewSessionSent; + if (isStatic) { + MixHash(out + offset, len + 16); // h = SHA256(h || ciphertext) + if (GetOwner()) { + auto tagsetNsr = std::make_shared(shared_from_this(), true); + InitNewSessionTagset(tagsetNsr); + tagsetNsr->Expire(); // let non-replied session expire + GenerateMoreReceiveTags(tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS); + } + } + return true; + } - bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { - // we are Bob - m_NSRSendTagset = std::make_shared(); - InitNewSessionTagset (m_NSRSendTagset); - uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); + bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage(const uint8_t *payload, size_t len, uint8_t *out, + size_t outLen) { + // we are Bob + m_NSRSendTagset = std::make_shared(); + InitNewSessionTagset(m_NSRSendTagset); + uint64_t tag = m_NSRSendTagset->GetNextSessionTag(); - size_t offset = 0; - memcpy (out + offset, &tag, 8); - offset += 8; - if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk - { - LogPrint (eLogError, "Garlic: Can't encode elligator"); - return false; - } - memcpy (m_NSREncodedKey, out + offset, 32); // for possible next NSR - memcpy (m_NSRH, m_H, 32); - offset += 32; - // KDF for Reply Key Section - MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) - MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) - uint8_t sharedSecret[32]; - if (!m_EphemeralKeys->Agree (m_Aepk, sharedSecret)) // sharedSecret = x25519(besk, aepk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); - return false; - } - MixKey (sharedSecret); - if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); - return false; - } - MixKey (sharedSecret); - uint8_t nonce[12]; - CreateNonce (0, nonce); - // calculate hash for zero length - if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) - { - LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); - return false; - } - MixHash (out + offset, 16); // h = SHA256(h || ciphertext) - offset += 16; - // KDF for payload - uint8_t keydata[64]; - i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) - // k_ab = keydata[0:31], k_ba = keydata[32:63] - auto receiveTagset = std::make_shared(shared_from_this()); - receiveTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) - receiveTagset->NextSessionTagRatchet (); - m_SendTagset = std::make_shared(); - m_SendTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) - m_SendTagset->NextSessionTagRatchet (); - GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? - GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS); - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) - // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed"); - return false; - } - m_State = eSessionStateNewSessionReplySent; - m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); + size_t offset = 0; + memcpy(out + offset, &tag, 8); + offset += 8; + if (!GenerateEphemeralKeysAndEncode(out + offset)) // bepk + { + LogPrint(eLogError, "Garlic: Can't encode elligator"); + return false; + } + memcpy(m_NSREncodedKey, out + offset, 32); // for possible next NSR + memcpy(m_NSRH, m_H, 32); + offset += 32; + // KDF for Reply Key Section + MixHash((const uint8_t *) &tag, 8); // h = SHA256(h || tag) + MixHash(m_EphemeralKeys->GetPublicKey(), 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + if (!m_EphemeralKeys->Agree(m_Aepk, sharedSecret)) // sharedSecret = x25519(besk, aepk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Alice ephemeral key"); + return false; + } + MixKey(sharedSecret); + if (!m_EphemeralKeys->Agree(m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Alice static key"); + return false; + } + MixKey(sharedSecret); + uint8_t nonce[12]; + CreateNonce(0, nonce); + // calculate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305(nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, + out + offset, 16, + true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + { + LogPrint(eLogWarning, "Garlic: Reply key section AEAD encryption failed"); + return false; + } + MixHash(out + offset, 16); // h = SHA256(h || ciphertext) + offset += 16; + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF(m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // k_ab = keydata[0:31], k_ba = keydata[32:63] + auto receiveTagset = std::make_shared(shared_from_this()); + receiveTagset->DHInitialize(m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + receiveTagset->NextSessionTagRatchet(); + m_SendTagset = std::make_shared(); + m_SendTagset->DHInitialize(m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + m_SendTagset->NextSessionTagRatchet(); + GenerateMoreReceiveTags(receiveTagset, (GetOwner() && GetOwner()->GetNumRatchetInboundTags() > 0) ? + GetOwner()->GetNumRatchetInboundTags() + : ECIESX25519_MIN_NUM_GENERATED_TAGS); + i2p::crypto::HKDF(keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, + 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, + true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: NSR payload section AEAD encryption failed"); + return false; + } + m_State = eSessionStateNewSessionReplySent; + m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch(); - return true; - } + return true; + } - bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { - // we are Bob and sent NSR already - uint64_t tag = m_NSRSendTagset->GetNextSessionTag (); // next tag - memcpy (out, &tag, 8); - memcpy (out + 8, m_NSREncodedKey, 32); - // recalculate h with new tag - memcpy (m_H, m_NSRH, 32); - MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) - MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) - uint8_t nonce[12]; - CreateNonce (0, nonce); - if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) - { - LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); - return false; - } - MixHash (out + 40, 16); // h = SHA256(h || ciphertext) - // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed"); - return false; - } - return true; - } + bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage(const uint8_t *payload, size_t len, uint8_t *out, + size_t outLen) { + // we are Bob and sent NSR already + uint64_t tag = m_NSRSendTagset->GetNextSessionTag(); // next tag + memcpy(out, &tag, 8); + memcpy(out + 8, m_NSREncodedKey, 32); + // recalculate h with new tag + memcpy(m_H, m_NSRH, 32); + MixHash((const uint8_t *) &tag, 8); // h = SHA256(h || tag) + MixHash(m_EphemeralKeys->GetPublicKey(), 32); // h = SHA256(h || bepk) + uint8_t nonce[12]; + CreateNonce(0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305(nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, + 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + { + LogPrint(eLogWarning, "Garlic: Reply key section AEAD encryption failed"); + return false; + } + MixHash(out + 40, 16); // h = SHA256(h || ciphertext) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, + true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed"); + return false; + } + return true; + } - bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (uint8_t * buf, size_t len) - { - // we are Alice - LogPrint (eLogDebug, "Garlic: Reply received"); - const uint8_t * tag = buf; - buf += 8; len -= 8; // tag - uint8_t bepk[32]; // Bob's ephemeral key - if (!i2p::crypto::GetElligator ()->Decode (buf, bepk)) - { - LogPrint (eLogError, "Garlic: Can't decode elligator"); - return false; - } - buf += 32; len -= 32; - // KDF for Reply Key Section - i2p::util::SaveStateHelper s(GetNoiseState ()); // restore noise state on exit - MixHash (tag, 8); // h = SHA256(h || tag) - MixHash (bepk, 32); // h = SHA256(h || bepk) - uint8_t sharedSecret[32]; - if (!m_EphemeralKeys->Agree (bepk, sharedSecret)) // sharedSecret = x25519(aesk, bepk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Bob ephemeral key"); - return false; - } - MixKey (sharedSecret); - GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) - MixKey (sharedSecret); + bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply(uint8_t *buf, size_t len) { + // we are Alice + LogPrint(eLogDebug, "Garlic: Reply received"); + const uint8_t *tag = buf; + buf += 8; + len -= 8; // tag + uint8_t bepk[32]; // Bob's ephemeral key + if (!i2p::crypto::GetElligator()->Decode(buf, bepk)) { + LogPrint(eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; + len -= 32; + // KDF for Reply Key Section + i2p::util::SaveStateHelper s( + GetNoiseState()); // restore noise state on exit + MixHash(tag, 8); // h = SHA256(h || tag) + MixHash(bepk, 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + if (!m_EphemeralKeys->Agree(bepk, sharedSecret)) // sharedSecret = x25519(aesk, bepk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Bob ephemeral key"); + return false; + } + MixKey(sharedSecret); + GetOwner()->Decrypt(bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) + MixKey(sharedSecret); - uint8_t nonce[12]; - CreateNonce (0, nonce); - // calculate hash for zero length - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only - { - LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed"); - return false; - } - MixHash (buf, 16); // h = SHA256(h || ciphertext) - buf += 16; len -= 16; - // KDF for payload - uint8_t keydata[64]; - i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) - if (m_State == eSessionStateNewSessionSent) - { - // only first time, then we keep using existing tagsets - // k_ab = keydata[0:31], k_ba = keydata[32:63] - m_SendTagset = std::make_shared(); - m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) - m_SendTagset->NextSessionTagRatchet (); - auto receiveTagset = std::make_shared(shared_from_this ()); - receiveTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) - receiveTagset->NextSessionTagRatchet (); - GenerateMoreReceiveTags (receiveTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? - GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS); - } - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) - // decrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); - return false; - } + uint8_t nonce[12]; + CreateNonce(0, nonce); + // calculate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305(buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, + 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only + { + LogPrint(eLogWarning, "Garlic: Reply key section AEAD decryption failed"); + return false; + } + MixHash(buf, 16); // h = SHA256(h || ciphertext) + buf += 16; + len -= 16; + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF(m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + if (m_State == eSessionStateNewSessionSent) { + // only first time, then we keep using existing tagsets + // k_ab = keydata[0:31], k_ba = keydata[32:63] + m_SendTagset = std::make_shared(); + m_SendTagset->DHInitialize(m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + m_SendTagset->NextSessionTagRatchet(); + auto receiveTagset = std::make_shared(shared_from_this()); + receiveTagset->DHInitialize(m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + receiveTagset->NextSessionTagRatchet(); + GenerateMoreReceiveTags(receiveTagset, (GetOwner() && GetOwner()->GetNumRatchetInboundTags() > 0) ? + GetOwner()->GetNumRatchetInboundTags() + : ECIESX25519_MIN_NUM_GENERATED_TAGS); + } + i2p::crypto::HKDF(keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, + 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // decrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305(buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, + false)) // decrypt + { + LogPrint(eLogWarning, "Garlic: Payload section AEAD decryption failed"); + return false; + } - if (m_State == eSessionStateNewSessionSent) - { - m_State = eSessionStateEstablished; - //m_EphemeralKeys = nullptr; // TODO: delete after a while - m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); - GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); - } - HandlePayload (buf, len - 16, nullptr, 0); + if (m_State == eSessionStateNewSessionSent) { + m_State = eSessionStateEstablished; + //m_EphemeralKeys = nullptr; // TODO: delete after a while + m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch(); + GetOwner()->AddECIESx25519Session(m_RemoteStaticKey, shared_from_this()); + } + HandlePayload(buf, len - 16, nullptr, 0); - // we have received reply to NS with LeaseSet in it - SetLeaseSetUpdateStatus (eLeaseSetUpToDate); - SetLeaseSetUpdateMsgID (0); + // we have received reply to NS with LeaseSet in it + SetLeaseSetUpdateStatus(eLeaseSetUpToDate); + SetLeaseSetUpdateMsgID(0); - return true; - } + return true; + } - bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { - uint8_t nonce[12]; - auto index = m_SendTagset->GetNextIndex (); - CreateNonce (index, nonce); // tag's index - uint64_t tag = m_SendTagset->GetNextSessionTag (); - if (!tag) - { - LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); - if (GetOwner ()) - GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); - return false; - } - memcpy (out, &tag, 8); - // ad = The session tag, 8 bytes - // ciphertext = ENCRYPT(k, n, payload, ad) - uint8_t key[32]; - m_SendTagset->GetSymmKey (index, key); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); - return false; - } - if (index >= ECIESX25519_TAGSET_MAX_NUM_TAGS && !m_SendForwardKey) - NewNextSendRatchet (); - return true; - } + bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, + size_t outLen) { + uint8_t nonce[12]; + auto index = m_SendTagset->GetNextIndex(); + CreateNonce(index, nonce); // tag's index + uint64_t tag = m_SendTagset->GetNextSessionTag(); + if (!tag) { + LogPrint(eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); + if (GetOwner()) + GetOwner()->RemoveECIESx25519Session(m_RemoteStaticKey); + return false; + } + memcpy(out, &tag, 8); + // ad = The session tag, 8 bytes + // ciphertext = ENCRYPT(k, n, payload, ad) + uint8_t key[32]; + m_SendTagset->GetSymmKey(index, key); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len, out, 8, key, nonce, out + 8, outLen - 8, + true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + if (index >= ECIESX25519_TAGSET_MAX_NUM_TAGS && !m_SendForwardKey) + NewNextSendRatchet(); + return true; + } - bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (uint8_t * buf, size_t len, - std::shared_ptr receiveTagset, int index) - { - uint8_t nonce[12]; - CreateNonce (index, nonce); // tag's index - len -= 8; // tag - uint8_t * payload = buf + 8; - uint8_t key[32]; - receiveTagset->GetSymmKey (index, key); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); - return false; - } - HandlePayload (payload, len - 16, receiveTagset, index); - if (GetOwner ()) - { - int moreTags = 0; - if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings? - { - if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2) - moreTags = GetOwner ()->GetNumRatchetInboundTags (); - index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind - } - else - { - moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 - if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; - moreTags -= (receiveTagset->GetNextIndex () - index); - index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind - } - if (moreTags > 0) - GenerateMoreReceiveTags (receiveTagset, moreTags); - if (index > 0) - receiveTagset->SetTrimBehind (index); - } - return true; - } + bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage(uint8_t *buf, size_t len, + std::shared_ptr receiveTagset, + int index) { + uint8_t nonce[12]; + CreateNonce(index, nonce); // tag's index + len -= 8; // tag + uint8_t *payload = buf + 8; + uint8_t key[32]; + receiveTagset->GetSymmKey(index, key); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 16, buf, 8, key, nonce, payload, len - 16, + false)) // decrypt + { + LogPrint(eLogWarning, "Garlic: Payload section AEAD decryption failed"); + return false; + } + HandlePayload(payload, len - 16, receiveTagset, index); + if (GetOwner()) { + int moreTags = 0; + if (GetOwner()->GetNumRatchetInboundTags() > 0) // override in settings? + { + if (receiveTagset->GetNextIndex() - index < GetOwner()->GetNumRatchetInboundTags() / 2) + moreTags = GetOwner()->GetNumRatchetInboundTags(); + index -= GetOwner()->GetNumRatchetInboundTags(); // trim behind + } else { + moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 + if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; + moreTags -= (receiveTagset->GetNextIndex() - index); + index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind + } + if (moreTags > 0) + GenerateMoreReceiveTags(receiveTagset, moreTags); + if (index > 0) + receiveTagset->SetTrimBehind(index); + } + return true; + } - bool ECIESX25519AEADRatchetSession::HandleNextMessage (uint8_t * buf, size_t len, - std::shared_ptr receiveTagset, int index) - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - switch (m_State) - { - case eSessionStateNewSessionReplySent: - m_State = eSessionStateEstablished; - m_NSRSendTagset = nullptr; - m_EphemeralKeys = nullptr; + bool ECIESX25519AEADRatchetSession::HandleNextMessage(uint8_t *buf, size_t len, + std::shared_ptr receiveTagset, + int index) { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + switch (m_State) { + case eSessionStateNewSessionReplySent: + m_State = eSessionStateEstablished; + m_NSRSendTagset = nullptr; + m_EphemeralKeys = nullptr; #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; + [[fallthrough]]; #endif - case eSessionStateEstablished: - if (receiveTagset->IsNS ()) - { - // our of sequence NSR - LogPrint (eLogDebug, "Garlic: Check for out of order NSR with index ", index); - if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2) - GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS); - return HandleNewOutgoingSessionReply (buf, len); - } - else - return HandleExistingSessionMessage (buf, len, receiveTagset, index); - case eSessionStateNew: - return HandleNewIncomingSession (buf, len); - case eSessionStateNewSessionSent: - return HandleNewOutgoingSessionReply (buf, len); - default: - return false; - } - return true; - } + case eSessionStateEstablished: + if (receiveTagset->IsNS()) { + // our of sequence NSR + LogPrint(eLogDebug, "Garlic: Check for out of order NSR with index ", index); + if (receiveTagset->GetNextIndex() - index < ECIESX25519_NSR_NUM_GENERATED_TAGS / 2) + GenerateMoreReceiveTags(receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS); + return HandleNewOutgoingSessionReply(buf, len); + } else + return HandleExistingSessionMessage(buf, len, receiveTagset, index); + case eSessionStateNew: + return HandleNewIncomingSession(buf, len); + case eSessionStateNewSessionSent: + return HandleNewOutgoingSessionReply(buf, len); + default: + return false; + } + return true; + } - std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) - { - uint8_t * payload = GetOwner ()->GetPayloadBuffer (); - if (!payload) return nullptr; - size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload); - if (!len) return nullptr; - auto m = NewI2NPMessage (len + 100); // 96 + 4 - m->Align (12); // in order to get buf aligned to 16 (12 + 4) - uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + std::shared_ptr + ECIESX25519AEADRatchetSession::WrapSingleMessage(std::shared_ptr msg) { + uint8_t *payload = GetOwner()->GetPayloadBuffer(); + if (!payload) return nullptr; + size_t len = CreatePayload(msg, m_State != eSessionStateEstablished, payload); + if (!len) return nullptr; + auto m = NewI2NPMessage(len + 100); // 96 + 4 + m->Align(12); // in order to get buf aligned to 16 (12 + 4) + uint8_t *buf = m->GetPayload() + 4; // 4 bytes for length - switch (m_State) - { - case eSessionStateEstablished: - if (!NewExistingSessionMessage (payload, len, buf, m->maxLen)) - return nullptr; - len += 24; - break; - case eSessionStateNew: - if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen)) - return nullptr; - len += 96; - break; - case eSessionStateNewSessionReceived: - if (!NewSessionReplyMessage (payload, len, buf, m->maxLen)) - return nullptr; - len += 72; - break; - case eSessionStateNewSessionReplySent: - if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen)) - return nullptr; - len += 72; - break; - case eSessionStateOneTime: - if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false)) - return nullptr; - len += 96; - break; - default: - return nullptr; - } + switch (m_State) { + case eSessionStateEstablished: + if (!NewExistingSessionMessage(payload, len, buf, m->maxLen)) + return nullptr; + len += 24; + break; + case eSessionStateNew: + if (!NewOutgoingSessionMessage(payload, len, buf, m->maxLen)) + return nullptr; + len += 96; + break; + case eSessionStateNewSessionReceived: + if (!NewSessionReplyMessage(payload, len, buf, m->maxLen)) + return nullptr; + len += 72; + break; + case eSessionStateNewSessionReplySent: + if (!NextNewSessionReplyMessage(payload, len, buf, m->maxLen)) + return nullptr; + len += 72; + break; + case eSessionStateOneTime: + if (!NewOutgoingSessionMessage(payload, len, buf, m->maxLen, false)) + return nullptr; + len += 96; + break; + default: + return nullptr; + } - htobe32buf (m->GetPayload (), len); - m->len += len + 4; - m->FillI2NPMessageHeader (eI2NPGarlic); - return m; - } + htobe32buf(m->GetPayload(), len); + m->len += len + 4; + m->FillI2NPMessageHeader(eI2NPGarlic); + return m; + } - std::shared_ptr ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr msg) - { - m_State = eSessionStateOneTime; - return WrapSingleMessage (msg); - } + std::shared_ptr + ECIESX25519AEADRatchetSession::WrapOneTimeMessage(std::shared_ptr msg) { + m_State = eSessionStateOneTime; + return WrapSingleMessage(msg); + } - size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg, bool first, uint8_t * payload) - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - size_t payloadLen = 0; - if (first) payloadLen += 7;// datatime - if (msg) - { - payloadLen += msg->GetPayloadLength () + 13; - if (m_Destination) payloadLen += 32; - } - if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) - { - // resubmit non-confirmed LeaseSet - SetLeaseSetUpdateStatus (eLeaseSetUpdated); - SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed - } - auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? GetOwner ()->GetLeaseSet () : nullptr; - if (leaseSet) - { - payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13; - if (!first) - { - // ack request - SetLeaseSetUpdateStatus (eLeaseSetSubmitted); - SetLeaseSetUpdateMsgID (m_SendTagset->GetNextIndex ()); - SetLeaseSetSubmissionTime (ts); - payloadLen += 4; - } - } - if (m_AckRequests.size () > 0) - payloadLen += m_AckRequests.size ()*4 + 3; - if (m_SendReverseKey) - { - payloadLen += 6; - if (m_NextReceiveRatchet->newKey) payloadLen += 32; - } - if (m_SendForwardKey) - { - payloadLen += 6; - if (m_NextSendRatchet->newKey) payloadLen += 32; - } - uint8_t paddingSize = 0; - if (payloadLen || ts > m_LastSentTimestamp + ECIESX25519_SEND_INACTIVITY_TIMEOUT) - { - int delta = (int)ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int)payloadLen; - if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size - { - paddingSize = m_PaddingSizes[m_NextPaddingSize++] & 0x0F; // 0 - 15 - if (m_NextPaddingSize >= 32) - { - RAND_bytes (m_PaddingSizes, 32); - m_NextPaddingSize = 0; - } - if (delta > 3) - { - delta -= 3; - if (paddingSize >= delta) paddingSize %= delta; - } - paddingSize++; - payloadLen += paddingSize + 3; - } - } - if (payloadLen) - { - if (payloadLen > I2NP_MAX_MESSAGE_SIZE) - { - LogPrint (eLogError, "Garlic: Payload length ", payloadLen, " is too long"); - return 0; - } - m_LastSentTimestamp = ts; - size_t offset = 0; - // DateTime - if (first) - { - payload[offset] = eECIESx25519BlkDateTime; offset++; - htobe16buf (payload + offset, 4); offset += 2; - htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds - } - // LeaseSet - if (leaseSet) - { - offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset); - if (!first) - { - // ack request - payload[offset] = eECIESx25519BlkAckRequest; offset++; - htobe16buf (payload + offset, 1); offset += 2; - payload[offset] = 0; offset++; // flags - } - } - // msg - if (msg) - offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset); - // ack - if (m_AckRequests.size () > 0) - { - payload[offset] = eECIESx25519BlkAck; offset++; - htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2; - for (auto& it: m_AckRequests) - { - htobe16buf (payload + offset, it.first); offset += 2; - htobe16buf (payload + offset, it.second); offset += 2; - } - m_AckRequests.clear (); - } - // next keys - if (m_SendReverseKey) - { - payload[offset] = eECIESx25519BlkNextKey; offset++; - htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2; - payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; - int keyID = m_NextReceiveRatchet->keyID - 1; - if (m_NextReceiveRatchet->newKey) - { - payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; - keyID++; - } - offset++; // flag - htobe16buf (payload + offset, keyID); offset += 2; // keyid - if (m_NextReceiveRatchet->newKey) - { - memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32); - offset += 32; // public key - } - m_SendReverseKey = false; - } - if (m_SendForwardKey) - { - payload[offset] = eECIESx25519BlkNextKey; offset++; - htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2; - payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; - if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only - offset++; // flag - htobe16buf (payload + offset, m_NextSendRatchet->keyID); offset += 2; // keyid - if (m_NextSendRatchet->newKey) - { - memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32); - offset += 32; // public key - } - } - // padding - if (paddingSize) - { - payload[offset] = eECIESx25519BlkPadding; offset++; - htobe16buf (payload + offset, paddingSize); offset += 2; - memset (payload + offset, 0, paddingSize); offset += paddingSize; - } - } - return payloadLen; - } + size_t ECIESX25519AEADRatchetSession::CreatePayload(std::shared_ptr msg, bool first, + uint8_t *payload) { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + size_t payloadLen = 0; + if (first) payloadLen += 7;// datatime + if (msg) { + payloadLen += msg->GetPayloadLength() + 13; + if (m_Destination) payloadLen += 32; + } + if (GetLeaseSetUpdateStatus() == eLeaseSetSubmitted && + ts > GetLeaseSetSubmissionTime() + LEASET_CONFIRMATION_TIMEOUT) { + // resubmit non-confirmed LeaseSet + SetLeaseSetUpdateStatus(eLeaseSetUpdated); + SetSharedRoutingPath(nullptr); // invalidate path since leaseset was not confirmed + } + auto leaseSet = (GetLeaseSetUpdateStatus() == eLeaseSetUpdated) ? GetOwner()->GetLeaseSet() : nullptr; + if (leaseSet) { + payloadLen += leaseSet->GetBufferLen() + DATABASE_STORE_HEADER_SIZE + 13; + if (!first) { + // ack request + SetLeaseSetUpdateStatus(eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID(m_SendTagset->GetNextIndex()); + SetLeaseSetSubmissionTime(ts); + payloadLen += 4; + } + } + if (m_AckRequests.size() > 0) + payloadLen += m_AckRequests.size() * 4 + 3; + if (m_SendReverseKey) { + payloadLen += 6; + if (m_NextReceiveRatchet->newKey) payloadLen += 32; + } + if (m_SendForwardKey) { + payloadLen += 6; + if (m_NextSendRatchet->newKey) payloadLen += 32; + } + uint8_t paddingSize = 0; + if (payloadLen || ts > m_LastSentTimestamp + ECIESX25519_SEND_INACTIVITY_TIMEOUT) { + int delta = (int) ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int) payloadLen; + if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size + { + paddingSize = m_PaddingSizes[m_NextPaddingSize++] & 0x0F; // 0 - 15 + if (m_NextPaddingSize >= 32) { + RAND_bytes(m_PaddingSizes, 32); + m_NextPaddingSize = 0; + } + if (delta > 3) { + delta -= 3; + if (paddingSize >= delta) paddingSize %= delta; + } + paddingSize++; + payloadLen += paddingSize + 3; + } + } + if (payloadLen) { + if (payloadLen > I2NP_MAX_MESSAGE_SIZE) { + LogPrint(eLogError, "Garlic: Payload length ", payloadLen, " is too long"); + return 0; + } + m_LastSentTimestamp = ts; + size_t offset = 0; + // DateTime + if (first) { + payload[offset] = eECIESx25519BlkDateTime; + offset++; + htobe16buf(payload + offset, 4); + offset += 2; + htobe32buf(payload + offset, ts / 1000); + offset += 4; // in seconds + } + // LeaseSet + if (leaseSet) { + offset += CreateLeaseSetClove(leaseSet, ts, payload + offset, payloadLen - offset); + if (!first) { + // ack request + payload[offset] = eECIESx25519BlkAckRequest; + offset++; + htobe16buf(payload + offset, 1); + offset += 2; + payload[offset] = 0; + offset++; // flags + } + } + // msg + if (msg) + offset += CreateGarlicClove(msg, payload + offset, payloadLen - offset); + // ack + if (m_AckRequests.size() > 0) { + payload[offset] = eECIESx25519BlkAck; + offset++; + htobe16buf(payload + offset, m_AckRequests.size() * 4); + offset += 2; + for (auto &it: m_AckRequests) { + htobe16buf(payload + offset, it.first); + offset += 2; + htobe16buf(payload + offset, it.second); + offset += 2; + } + m_AckRequests.clear(); + } + // next keys + if (m_SendReverseKey) { + payload[offset] = eECIESx25519BlkNextKey; + offset++; + htobe16buf(payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); + offset += 2; + payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; + int keyID = m_NextReceiveRatchet->keyID - 1; + if (m_NextReceiveRatchet->newKey) { + payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; + keyID++; + } + offset++; // flag + htobe16buf(payload + offset, keyID); + offset += 2; // keyid + if (m_NextReceiveRatchet->newKey) { + memcpy(payload + offset, m_NextReceiveRatchet->key->GetPublicKey(), 32); + offset += 32; // public key + } + m_SendReverseKey = false; + } + if (m_SendForwardKey) { + payload[offset] = eECIESx25519BlkNextKey; + offset++; + htobe16buf(payload + offset, m_NextSendRatchet->newKey ? 35 : 3); + offset += 2; + payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG + : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; + if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only + offset++; // flag + htobe16buf(payload + offset, m_NextSendRatchet->keyID); + offset += 2; // keyid + if (m_NextSendRatchet->newKey) { + memcpy(payload + offset, m_NextSendRatchet->key->GetPublicKey(), 32); + offset += 32; // public key + } + } + // padding + if (paddingSize) { + payload[offset] = eECIESx25519BlkPadding; + offset++; + htobe16buf(payload + offset, paddingSize); + offset += 2; + memset(payload + offset, 0, paddingSize); + offset += paddingSize; + } + } + return payloadLen; + } - size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len) - { - if (!msg) return 0; - uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; - if (m_Destination) cloveSize += 32; - if ((int)len < cloveSize + 3) return 0; - buf[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (buf + 1, cloveSize); // size - buf += 3; - if (m_Destination) - { - *buf = (eGarlicDeliveryTypeDestination << 5); - memcpy (buf + 1, *m_Destination, 32); buf += 32; - } - else - *buf = 0; - buf++; // flag and delivery instructions - *buf = msg->GetTypeID (); // I2NP msg type - htobe32buf (buf + 1, msg->GetMsgID ()); // msgID - htobe32buf (buf + 5, msg->GetExpiration () / 1000); // expiration in seconds - memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); - return cloveSize + 3; - } + size_t ECIESX25519AEADRatchetSession::CreateGarlicClove(std::shared_ptr msg, uint8_t *buf, + size_t len) { + if (!msg) return 0; + uint16_t cloveSize = msg->GetPayloadLength() + 9 + 1; + if (m_Destination) cloveSize += 32; + if ((int) len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf(buf + 1, cloveSize); // size + buf += 3; + if (m_Destination) { + *buf = (eGarlicDeliveryTypeDestination << 5); + memcpy(buf + 1, *m_Destination, 32); + buf += 32; + } else + *buf = 0; + buf++; // flag and delivery instructions + *buf = msg->GetTypeID(); // I2NP msg type + htobe32buf(buf + 1, msg->GetMsgID()); // msgID + htobe32buf(buf + 5, msg->GetExpiration() / 1000); // expiration in seconds + memcpy(buf + 9, msg->GetPayload(), msg->GetPayloadLength()); + return cloveSize + 3; + } - size_t ECIESX25519AEADRatchetSession::CreateLeaseSetClove (std::shared_ptr ls, uint64_t ts, uint8_t * buf, size_t len) - { - if (!ls || ls->GetStoreType () != i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) - { - LogPrint (eLogError, "Garlic: Incorrect LeasetSet type to send"); - return 0; - } - uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls->GetBufferLen (); // to local - if ((int)len < cloveSize + 3) return 0; - buf[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (buf + 1, cloveSize); // size - buf += 3; - *buf = 0; buf++; // flag and delivery instructions - *buf = eI2NPDatabaseStore; buf++; // I2NP msg type - RAND_bytes (buf, 4); buf += 4; // msgID - htobe32buf (buf, (ts + I2NP_MESSAGE_EXPIRATION_TIMEOUT)/1000); buf += 4; // expiration - // payload - memcpy (buf + DATABASE_STORE_KEY_OFFSET, ls->GetStoreHash (), 32); - buf[DATABASE_STORE_TYPE_OFFSET] = i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2; - memset (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0, 4); // replyToken = 0 - buf += DATABASE_STORE_HEADER_SIZE; - memcpy (buf, ls->GetBuffer (), ls->GetBufferLen ()); + size_t ECIESX25519AEADRatchetSession::CreateLeaseSetClove(std::shared_ptr ls, + uint64_t ts, uint8_t *buf, size_t len) { + if (!ls || ls->GetStoreType() != i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) { + LogPrint(eLogError, "Garlic: Incorrect LeasetSet type to send"); + return 0; + } + uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls->GetBufferLen(); // to local + if ((int) len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf(buf + 1, cloveSize); // size + buf += 3; + *buf = 0; + buf++; // flag and delivery instructions + *buf = eI2NPDatabaseStore; + buf++; // I2NP msg type + RAND_bytes(buf, 4); + buf += 4; // msgID + htobe32buf(buf, (ts + I2NP_MESSAGE_EXPIRATION_TIMEOUT) / 1000); + buf += 4; // expiration + // payload + memcpy(buf + DATABASE_STORE_KEY_OFFSET, ls->GetStoreHash(), 32); + buf[DATABASE_STORE_TYPE_OFFSET] = i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2; + memset(buf + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0, 4); // replyToken = 0 + buf += DATABASE_STORE_HEADER_SIZE; + memcpy(buf, ls->GetBuffer(), ls->GetBufferLen()); - return cloveSize + 3; - } + return cloveSize + 3; + } - void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr receiveTagset, int numTags) - { - if (GetOwner ()) - { - for (int i = 0; i < numTags; i++) - { - auto tag = GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset); - if (!tag) - { - LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset"); - break; - } - } - } - } + void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags(std::shared_ptr receiveTagset, + int numTags) { + if (GetOwner()) { + for (int i = 0; i < numTags; i++) { + auto tag = GetOwner()->AddECIESx25519SessionNextTag(receiveTagset); + if (!tag) { + LogPrint(eLogError, + "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset"); + break; + } + } + } + } - bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) - { - CleanupUnconfirmedLeaseSet (ts); - return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds - ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds - } + bool ECIESX25519AEADRatchetSession::CheckExpired(uint64_t ts) { + CleanupUnconfirmedLeaseSet(ts); + return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds + ts * 1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT * 1000; // milliseconds + } - RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState): - ECIESX25519AEADRatchetSession (&i2p::context, false) - { - SetLeaseSetUpdateStatus (eLeaseSetDoNotSend); - SetNoiseState (initState); - } + RouterIncomingRatchetSession::RouterIncomingRatchetSession(const i2p::crypto::NoiseSymmetricState &initState) : + ECIESX25519AEADRatchetSession(&i2p::context, false) { + SetLeaseSetUpdateStatus(eLeaseSetDoNotSend); + SetNoiseState(initState); + } - bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len) - { - if (!GetOwner ()) return false; - m_CurrentNoiseState = GetNoiseState (); - // we are Bob - m_CurrentNoiseState.MixHash (buf, 32); - uint8_t sharedSecret[32]; - if (!GetOwner ()->Decrypt (buf, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) - { - LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); - return false; - } - m_CurrentNoiseState.MixKey (sharedSecret); - buf += 32; len -= 32; - uint8_t nonce[12]; - CreateNonce (0, nonce); - std::vector payload (len - 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32, - m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed"); - return false; - } - HandlePayload (payload.data (), len - 16, nullptr, 0); - return true; - } + bool RouterIncomingRatchetSession::HandleNextMessage(const uint8_t *buf, size_t len) { + if (!GetOwner()) return false; + m_CurrentNoiseState = GetNoiseState(); + // we are Bob + m_CurrentNoiseState.MixHash(buf, 32); + uint8_t sharedSecret[32]; + if (!GetOwner()->Decrypt(buf, sharedSecret, + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) + { + LogPrint(eLogWarning, "Garlic: Incorrect N ephemeral public key"); + return false; + } + m_CurrentNoiseState.MixKey(sharedSecret); + buf += 32; + len -= 32; + uint8_t nonce[12]; + CreateNonce(0, nonce); + std::vector payload(len - 16); + if (!i2p::crypto::AEADChaCha20Poly1305(buf, len - 16, m_CurrentNoiseState.m_H, 32, + m_CurrentNoiseState.m_CK + 32, nonce, payload.data(), len - 16, + false)) // decrypt + { + LogPrint(eLogWarning, "Garlic: Payload for router AEAD verification failed"); + return false; + } + HandlePayload(payload.data(), len - 16, nullptr, 0); + return true; + } - static size_t CreateGarlicPayload (std::shared_ptr msg, uint8_t * payload, - bool datetime, size_t optimalSize) - { - size_t len = 0; - if (datetime) - { - // DateTime - payload[0] = eECIESx25519BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); - len = 7; - } - // I2NP - payload += len; - uint16_t cloveSize = msg->GetPayloadLength () + 10; - payload[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (payload + 1, cloveSize); // size - payload += 3; - payload[0] = 0; // flag and delivery instructions - payload[1] = msg->GetTypeID (); // I2NP msg type - htobe32buf (payload + 2, msg->GetMsgID ()); // msgID - htobe32buf (payload + 6, msg->GetExpiration () / 1000); // expiration in seconds - memcpy (payload + 10, msg->GetPayload (), msg->GetPayloadLength ()); - len += cloveSize + 3; - payload += cloveSize; - // padding - int delta = (int)optimalSize - (int)len; - if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size - { - uint8_t paddingSize = rand () & 0x0F; // 0 - 15 - if (delta > 3) - { - delta -= 3; - if (paddingSize > delta) paddingSize %= delta; - } - payload[0] = eECIESx25519BlkPadding; - htobe16buf (payload + 1, paddingSize); - if (paddingSize) memset (payload + 3, 0, paddingSize); - len += paddingSize + 3; - } - return len; - } + static size_t CreateGarlicPayload(std::shared_ptr msg, uint8_t *payload, + bool datetime, size_t optimalSize) { + size_t len = 0; + if (datetime) { + // DateTime + payload[0] = eECIESx25519BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, i2p::util::GetSecondsSinceEpoch()); + len = 7; + } + // I2NP + payload += len; + uint16_t cloveSize = msg->GetPayloadLength() + 10; + payload[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf(payload + 1, cloveSize); // size + payload += 3; + payload[0] = 0; // flag and delivery instructions + payload[1] = msg->GetTypeID(); // I2NP msg type + htobe32buf(payload + 2, msg->GetMsgID()); // msgID + htobe32buf(payload + 6, msg->GetExpiration() / 1000); // expiration in seconds + memcpy(payload + 10, msg->GetPayload(), msg->GetPayloadLength()); + len += cloveSize + 3; + payload += cloveSize; + // padding + int delta = (int) optimalSize - (int) len; + if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size + { + uint8_t paddingSize = rand() & 0x0F; // 0 - 15 + if (delta > 3) { + delta -= 3; + if (paddingSize > delta) paddingSize %= delta; + } + payload[0] = eECIESx25519BlkPadding; + htobe16buf(payload + 1, paddingSize); + if (paddingSize) memset(payload + 3, 0, paddingSize); + len += paddingSize + 3; + } + return len; + } - std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag) - { - auto m = NewI2NPMessage (); - m->Align (12); // in order to get buf aligned to 16 (12 + 4) - uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length - size_t offset = 0; - memcpy (buf + offset, &tag, 8); offset += 8; - auto payload = buf + offset; - size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery - uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); - return nullptr; - } - offset += len + 16; - htobe32buf (m->GetPayload (), offset); - m->len += offset + 4; - m->FillI2NPMessageHeader (eI2NPGarlic); - return m; - } + std::shared_ptr + WrapECIESX25519Message(std::shared_ptr msg, const uint8_t *key, uint64_t tag) { + auto m = NewI2NPMessage(); + m->Align(12); // in order to get buf aligned to 16 (12 + 4) + uint8_t *buf = m->GetPayload() + 4; // 4 bytes for length + size_t offset = 0; + memcpy(buf + offset, &tag, 8); + offset += 8; + auto payload = buf + offset; + size_t len = CreateGarlicPayload(msg, payload, false, + 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery + uint8_t nonce[12]; + memset(nonce, 0, 12); // n = 0 + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len, buf, 8, key, nonce, payload, len + 16, + true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return nullptr; + } + offset += len + 16; + htobe32buf(m->GetPayload(), offset); + m->len += offset + 4; + m->FillI2NPMessageHeader(eI2NPGarlic); + return m; + } - std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey) - { - // Noise_N, we are Alice, routerPublicKey is Bob's - i2p::crypto::NoiseSymmetricState noiseState; - i2p::crypto::InitNoiseNState (noiseState, routerPublicKey); - auto m = NewI2NPMessage (); - m->Align (12); // in order to get buf aligned to 16 (12 + 4) - uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length - size_t offset = 0; - auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - memcpy (buf + offset, ephemeralKeys->GetPublicKey (), 32); - noiseState.MixHash (buf + offset, 32); // h = SHA256(h || aepk) - offset += 32; - uint8_t sharedSecret[32]; - if (!ephemeralKeys->Agree (routerPublicKey, sharedSecret)) // x25519(aesk, bpk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); - return nullptr; - } - noiseState.MixKey (sharedSecret); - auto payload = buf + offset; - size_t len = CreateGarlicPayload (msg, payload, true, 900); // 1003 - 32 eph key - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 35 router tunnel delivery - uint8_t nonce[12]; - memset (nonce, 0, 12); - // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, payload, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed"); - return nullptr; - } - offset += len + 16; - htobe32buf (m->GetPayload (), offset); - m->len += offset + 4; - m->FillI2NPMessageHeader (eI2NPGarlic); - return m; - } -} + std::shared_ptr + WrapECIESX25519MessageForRouter(std::shared_ptr msg, const uint8_t *routerPublicKey) { + // Noise_N, we are Alice, routerPublicKey is Bob's + i2p::crypto::NoiseSymmetricState noiseState; + i2p::crypto::InitNoiseNState(noiseState, routerPublicKey); + auto m = NewI2NPMessage(); + m->Align(12); // in order to get buf aligned to 16 (12 + 4) + uint8_t *buf = m->GetPayload() + 4; // 4 bytes for length + size_t offset = 0; + auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair(); + memcpy(buf + offset, ephemeralKeys->GetPublicKey(), 32); + noiseState.MixHash(buf + offset, 32); // h = SHA256(h || aepk) + offset += 32; + uint8_t sharedSecret[32]; + if (!ephemeralKeys->Agree(routerPublicKey, sharedSecret)) // x25519(aesk, bpk) + { + LogPrint(eLogWarning, "Garlic: Incorrect Bob static key"); + return nullptr; + } + noiseState.MixKey(sharedSecret); + auto payload = buf + offset; + size_t len = CreateGarlicPayload(msg, payload, true, + 900); // 1003 - 32 eph key - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 35 router tunnel delivery + uint8_t nonce[12]; + memset(nonce, 0, 12); + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, + payload, len + 16, true)) // encrypt + { + LogPrint(eLogWarning, "Garlic: Payload for router AEAD encryption failed"); + return nullptr; + } + offset += len + 16; + htobe32buf(m->GetPayload(), offset); + m->len += offset + 4; + m->FillI2NPMessageHeader(eI2NPGarlic); + return m; + } + } } diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 301f597a..0a36fe47 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -21,233 +21,276 @@ #include "Garlic.h" #include "Tag.h" -namespace i2p -{ -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_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after - const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds - const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds - const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 - const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after - const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; - const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320; - const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; +namespace i2p { + 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_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after + const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds + const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds + const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 + const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after + const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; + const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320; + const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; - const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ - // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */ + const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ + // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */ - class RatchetTagSet - { - public: + class RatchetTagSet { + public: - RatchetTagSet () {}; - virtual ~RatchetTagSet () {}; + RatchetTagSet() {}; - void DHInitialize (const uint8_t * rootKey, const uint8_t * k); - void NextSessionTagRatchet (); - uint64_t GetNextSessionTag (); - const uint8_t * GetNextRootKey () const { return m_NextRootKey; }; - int GetNextIndex () const { return m_NextIndex; }; - void GetSymmKey (int index, uint8_t * key); - void DeleteSymmKey (int index); + virtual ~RatchetTagSet() {}; - int GetTagSetID () const { return m_TagSetID; }; - void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; }; + void DHInitialize(const uint8_t *rootKey, const uint8_t *k); - private: + void NextSessionTagRatchet(); - i2p::data::Tag<64> m_SessionTagKeyData; - uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; - int m_NextIndex, m_NextSymmKeyIndex; - std::unordered_map > m_ItermediateSymmKeys; + uint64_t GetNextSessionTag(); - int m_TagSetID = 0; - }; + const uint8_t *GetNextRootKey() const { return m_NextRootKey; }; - class ECIESX25519AEADRatchetSession; - class ReceiveRatchetTagSet: public RatchetTagSet, - public std::enable_shared_from_this - { - public: + int GetNextIndex() const { return m_NextIndex; }; - ReceiveRatchetTagSet (std::shared_ptr session, bool isNS = false): - m_Session (session), m_IsNS (isNS) {}; + void GetSymmKey(int index, uint8_t *key); - bool IsNS () const { return m_IsNS; }; - std::shared_ptr GetSession () { return m_Session; }; - void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; }; - int GetTrimBehind () const { return m_TrimBehindIndex; }; + void DeleteSymmKey(int index); - void Expire (); - bool IsExpired (uint64_t ts) const; + int GetTagSetID() const { return m_TagSetID; }; - virtual bool IsIndexExpired (int index) const; - virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index); + void SetTagSetID(int tagsetID) { m_TagSetID = tagsetID; }; - private: + private: - int m_TrimBehindIndex = 0; - std::shared_ptr m_Session; - bool m_IsNS; - uint64_t m_ExpirationTimestamp = 0; - }; + i2p::data::Tag<64> m_SessionTagKeyData; + uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; + int m_NextIndex, m_NextSymmKeyIndex; + std::unordered_map > m_ItermediateSymmKeys; - class SymmetricKeyTagSet: public ReceiveRatchetTagSet - { - public: + int m_TagSetID = 0; + }; - SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); + class ECIESX25519AEADRatchetSession; - bool IsIndexExpired (int index) const { return false; }; - bool HandleNextMessage (uint8_t * buf, size_t len, int index); + class ReceiveRatchetTagSet : public RatchetTagSet, + public std::enable_shared_from_this { + public: - private: + ReceiveRatchetTagSet(std::shared_ptr session, bool isNS = false) : + m_Session(session), m_IsNS(isNS) {}; - GarlicDestination * m_Destination; - uint8_t m_Key[32]; - }; + bool IsNS() const { return m_IsNS; }; - enum ECIESx25519BlockType - { - eECIESx25519BlkDateTime = 0, - eECIESx25519BlkSessionID = 1, - eECIESx25519BlkTermination = 4, - eECIESx25519BlkOptions = 5, - eECIESx25519BlkNextKey = 7, - eECIESx25519BlkAck = 8, - eECIESx25519BlkAckRequest = 9, - eECIESx25519BlkGalicClove = 11, - eECIESx25519BlkPadding = 254 - }; + std::shared_ptr GetSession() { return m_Session; }; - const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01; - const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; - const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; + void SetTrimBehind(int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; }; - class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, - private i2p::crypto::NoiseSymmetricState, - public std::enable_shared_from_this - { - enum SessionState - { - eSessionStateNew = 0, - eSessionStateNewSessionReceived, - eSessionStateNewSessionSent, - eSessionStateNewSessionReplySent, - eSessionStateEstablished, - eSessionStateOneTime - }; + int GetTrimBehind() const { return m_TrimBehindIndex; }; - struct DHRatchet - { - int keyID = 0; - std::shared_ptr key; - uint8_t remote[32]; // last remote public key - bool newKey = true; - }; + void Expire(); - public: + bool IsExpired(uint64_t ts) const; - ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS); - ~ECIESX25519AEADRatchetSession (); + virtual bool IsIndexExpired(int index) const; - bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg); + virtual bool HandleNextMessage(uint8_t *buf, size_t len, int index); - const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } - void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } + private: - void Terminate () { m_IsTerminated = true; } - void SetDestination (const i2p::data::IdentHash& dest) // TODO: - { - if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); - } + int m_TrimBehindIndex = 0; + std::shared_ptr m_Session; + bool m_IsNS; + uint64_t m_ExpirationTimestamp = 0; + }; - 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); } + class SymmetricKeyTagSet : public ReceiveRatchetTagSet { + public: - 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; }; + SymmetricKeyTagSet(GarlicDestination *destination, const uint8_t *key); - protected: + bool IsIndexExpired(int index) const { return false; }; - 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& receiveTagset, int index); + bool HandleNextMessage(uint8_t *buf, size_t len, int index); - private: + private: - bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes - void InitNewSessionTagset (std::shared_ptr tagsetNsr) const; + GarlicDestination *m_Destination; + uint8_t m_Key[32]; + }; - 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 receiveTagset, int index); - void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset); + enum ECIESx25519BlockType { + eECIESx25519BlkDateTime = 0, + eECIESx25519BlkSessionID = 1, + 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); - 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); + const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01; + const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; + const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; - size_t CreatePayload (std::shared_ptr msg, bool first, uint8_t * payload); - size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len); - size_t CreateLeaseSetClove (std::shared_ptr ls, uint64_t ts, uint8_t * buf, size_t len); + class ECIESX25519AEADRatchetSession : public GarlicRoutingSession, + private i2p::crypto::NoiseSymmetricState, + public std::enable_shared_from_this { + enum SessionState { + eSessionStateNew = 0, + eSessionStateNewSessionReceived, + eSessionStateNewSessionSent, + eSessionStateNewSessionReplySent, + eSessionStateEstablished, + eSessionStateOneTime + }; - void GenerateMoreReceiveTags (std::shared_ptr receiveTagset, int numTags); - void NewNextSendRatchet (); + struct DHRatchet { + int keyID = 0; + std::shared_ptr key; + uint8_t remote[32]; // last remote public key + bool newKey = true; + }; - private: + public: - 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 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 m_SendTagset, m_NSRSendTagset; - std::unique_ptr m_Destination;// TODO: might not need it - std::list > m_AckRequests; // (tagsetid, index) - bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false; - std::unique_ptr m_NextReceiveRatchet, m_NextSendRatchet; - uint8_t m_PaddingSizes[32], m_NextPaddingSize; + ECIESX25519AEADRatchetSession(GarlicDestination *owner, bool attachLeaseSetNS); - public: + ~ECIESX25519AEADRatchetSession(); - // for HTTP only - int GetState () const { return (int)m_State; } - i2p::data::IdentHash GetDestination () const - { - return m_Destination ? *m_Destination : i2p::data::IdentHash (); - } - }; + bool HandleNextMessage(uint8_t *buf, size_t len, std::shared_ptr receiveTagset, + int index = 0); - // single session for all incoming messages - class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession - { - public: + std::shared_ptr WrapSingleMessage(std::shared_ptr msg); - RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); - bool HandleNextMessage (const uint8_t * buf, size_t len); - i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; + std::shared_ptr WrapOneTimeMessage(std::shared_ptr msg); - 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 WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag); - std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey); -} + void Terminate() { m_IsTerminated = true; } + + 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 &receiveTagset, + int index); + + private: + + bool GenerateEphemeralKeysAndEncode(uint8_t *buf); // buf is 32 bytes + void InitNewSessionTagset(std::shared_ptr 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 receiveTagset, + int index); + + void + HandleNextKey(const uint8_t *buf, size_t len, const std::shared_ptr &receiveTagset); + + bool NewOutgoingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen, + bool 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 msg, bool first, uint8_t *payload); + + size_t CreateGarlicClove(std::shared_ptr msg, uint8_t *buf, size_t len); + + size_t CreateLeaseSetClove(std::shared_ptr ls, uint64_t ts, uint8_t *buf, + size_t len); + + void GenerateMoreReceiveTags(std::shared_ptr 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 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 m_SendTagset, m_NSRSendTagset; + std::unique_ptr m_Destination;// TODO: might not need it + std::list > m_AckRequests; // (tagsetid, index) + bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false; + std::unique_ptr 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 + WrapECIESX25519Message(std::shared_ptr msg, const uint8_t *key, uint64_t tag); + + std::shared_ptr + WrapECIESX25519MessageForRouter(std::shared_ptr msg, const uint8_t *routerPublicKey); + } } #endif diff --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp index 0c6eb4f7..795eee11 100644 --- a/libi2pd/Ed25519.cpp +++ b/libi2pd/Ed25519.cpp @@ -11,597 +11,588 @@ #include "Crypto.h" #include "Ed25519.h" -namespace i2p -{ -namespace crypto -{ - Ed25519::Ed25519 () - { - BN_CTX * ctx = BN_CTX_new (); - BIGNUM * tmp = BN_new (); +namespace i2p { + namespace crypto { + Ed25519::Ed25519() { + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *tmp = BN_new(); - q = BN_new (); - // 2^255-19 - BN_set_bit (q, 255); // 2^255 - BN_sub_word (q, 19); + q = BN_new(); + // 2^255-19 + BN_set_bit(q, 255); // 2^255 + BN_sub_word(q, 19); - l = BN_new (); - // 2^252 + 27742317777372353535851937790883648493 - BN_set_bit (l, 252); - two_252_2 = BN_dup (l); - BN_dec2bn (&tmp, "27742317777372353535851937790883648493"); - BN_add (l, l, tmp); - BN_sub_word (two_252_2, 2); // 2^252 - 2 + l = BN_new(); + // 2^252 + 27742317777372353535851937790883648493 + BN_set_bit(l, 252); + two_252_2 = BN_dup(l); + BN_dec2bn(&tmp, "27742317777372353535851937790883648493"); + BN_add(l, l, tmp); + BN_sub_word(two_252_2, 2); // 2^252 - 2 - // -121665*inv(121666) - d = BN_new (); - BN_set_word (tmp, 121666); - BN_mod_inverse (tmp, tmp, q, ctx); - BN_set_word (d, 121665); - BN_set_negative (d, 1); - BN_mod_mul (d, d, tmp, q, ctx); + // -121665*inv(121666) + d = BN_new(); + BN_set_word(tmp, 121666); + BN_mod_inverse(tmp, tmp, q, ctx); + BN_set_word(d, 121665); + BN_set_negative(d, 1); + BN_mod_mul(d, d, tmp, q, ctx); - // 2^((q-1)/4) - I = BN_new (); - BN_free (tmp); - tmp = BN_dup (q); - BN_sub_word (tmp, 1); - BN_div_word (tmp, 4); - BN_set_word (I, 2); - BN_mod_exp (I, I, tmp, q, ctx); - BN_free (tmp); + // 2^((q-1)/4) + I = BN_new(); + BN_free(tmp); + tmp = BN_dup(q); + BN_sub_word(tmp, 1); + BN_div_word(tmp, 4); + BN_set_word(I, 2); + BN_mod_exp(I, I, tmp, q, ctx); + BN_free(tmp); - // 4*inv(5) - BIGNUM * By = BN_new (); - BN_set_word (By, 5); - BN_mod_inverse (By, By, q, ctx); - BN_mul_word (By, 4); - BIGNUM * Bx = RecoverX (By, ctx); - BN_mod (Bx, Bx, q, ctx); // % q - BN_mod (By, By, q, ctx); // % q + // 4*inv(5) + BIGNUM *By = BN_new(); + BN_set_word(By, 5); + BN_mod_inverse(By, By, q, ctx); + BN_mul_word(By, 4); + BIGNUM *Bx = RecoverX(By, ctx); + BN_mod(Bx, Bx, q, ctx); // % q + BN_mod(By, By, q, ctx); // % q - // precalculate Bi256 table - Bi256Carry = { Bx, By }; // B - for (int i = 0; i < 32; i++) - { - Bi256[i][0] = Bi256Carry; // first point - for (int j = 1; j < 128; j++) - Bi256[i][j] = Sum (Bi256[i][j-1], Bi256[i][0], ctx); // (256+j+1)^i*B - Bi256Carry = Bi256[i][127]; - for (int j = 0; j < 128; j++) // add first point 128 more times - Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx); - } + // precalculate Bi256 table + Bi256Carry = {Bx, By}; // B + for (int i = 0; i < 32; i++) { + Bi256[i][0] = Bi256Carry; // first point + for (int j = 1; j < 128; j++) + Bi256[i][j] = Sum(Bi256[i][j - 1], Bi256[i][0], ctx); // (256+j+1)^i*B + Bi256Carry = Bi256[i][127]; + for (int j = 0; j < 128; j++) // add first point 128 more times + Bi256Carry = Sum(Bi256Carry, Bi256[i][0], ctx); + } - BN_CTX_free (ctx); - } + BN_CTX_free(ctx); + } - Ed25519::Ed25519 (const Ed25519& other): q (BN_dup (other.q)), l (BN_dup (other.l)), - d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)), - Bi256Carry (other.Bi256Carry) - { - for (int i = 0; i < 32; i++) - for (int j = 0; j < 128; j++) - Bi256[i][j] = other.Bi256[i][j]; - } + Ed25519::Ed25519(const Ed25519 &other) : q(BN_dup(other.q)), l(BN_dup(other.l)), + d(BN_dup(other.d)), I(BN_dup(other.I)), + two_252_2(BN_dup(other.two_252_2)), + Bi256Carry(other.Bi256Carry) { + for (int i = 0; i < 32; i++) + for (int j = 0; j < 128; j++) + Bi256[i][j] = other.Bi256[i][j]; + } - Ed25519::~Ed25519 () - { - BN_free (q); - BN_free (l); - BN_free (d); - BN_free (I); - BN_free (two_252_2); - } + Ed25519::~Ed25519() { + BN_free(q); + BN_free(l); + BN_free(d); + BN_free(I); + BN_free(two_252_2); + } - EDDSAPoint Ed25519::GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const - { - return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian - } + EDDSAPoint Ed25519::GeneratePublicKey(const uint8_t *expandedPrivateKey, BN_CTX *ctx) const { + return MulB(expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian + } - EDDSAPoint Ed25519::DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const - { - return DecodePoint (buf, ctx); - } + EDDSAPoint Ed25519::DecodePublicKey(const uint8_t *buf, BN_CTX *ctx) const { + return DecodePoint(buf, ctx); + } - void Ed25519::EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const - { - EncodePoint (Normalize (publicKey, ctx), buf); - } + void Ed25519::EncodePublicKey(const EDDSAPoint &publicKey, uint8_t *buf, BN_CTX *ctx) const { + EncodePoint(Normalize(publicKey, ctx), buf); + } - bool Ed25519::Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const - { - BN_CTX * ctx = BN_CTX_new (); - BIGNUM * h = DecodeBN<64> (digest); - // signature 0..31 - R, 32..63 - S - // B*S = R + PK*h => R = B*S - PK*h - // we don't decode R, but encode (B*S - PK*h) - auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S; - BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0 - auto PKh = Mul (publicKey, h, ctx); // PK*h - uint8_t diff[32]; - EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded - bool passed = !memcmp (signature, diff, 32); // R - BN_free (h); - BN_CTX_free (ctx); - if (!passed) - LogPrint (eLogError, "25519 signature verification failed"); - return passed; - } + bool Ed25519::Verify(const EDDSAPoint &publicKey, const uint8_t *digest, const uint8_t *signature) const { + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *h = DecodeBN<64>(digest); + // signature 0..31 - R, 32..63 - S + // B*S = R + PK*h => R = B*S - PK*h + // we don't decode R, but encode (B*S - PK*h) + auto Bs = MulB(signature + EDDSA25519_SIGNATURE_LENGTH / 2, ctx); // B*S; + BN_mod(h, h, l, ctx); // public key is multiple of B, but B%l = 0 + auto PKh = Mul(publicKey, h, ctx); // PK*h + uint8_t diff[32]; + EncodePoint(Normalize(Sum(Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded + bool passed = !memcmp(signature, diff, 32); // R + BN_free(h); + BN_CTX_free(ctx); + if (!passed) + LogPrint(eLogError, "25519 signature verification failed"); + return passed; + } - void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, - const uint8_t * buf, size_t len, uint8_t * signature) const - { - BN_CTX * bnCtx = BN_CTX_new (); - // calculate r - SHA512_CTX ctx; - SHA512_Init (&ctx); - SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key - SHA512_Update (&ctx, buf, len); // data - uint8_t digest[64]; - SHA512_Final (digest, &ctx); - BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors - // calculate R - uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf - EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors - // calculate S - SHA512_Init (&ctx); - SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R - SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key - SHA512_Update (&ctx, buf, len); // data - SHA512_Final (digest, &ctx); - BIGNUM * h = DecodeBN<64> (digest); - // S = (r + h*a) % l - BIGNUM * a = DecodeBN (expandedPrivateKey); // left half of expanded key - BN_mod_mul (h, h, a, l, bnCtx); // %l - BN_mod_add (h, h, r, l, bnCtx); // %l - memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2); - EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S - BN_free (r); BN_free (h); BN_free (a); - BN_CTX_free (bnCtx); - } + void Ed25519::Sign(const uint8_t *expandedPrivateKey, const uint8_t *publicKeyEncoded, + const uint8_t *buf, size_t len, uint8_t *signature) const { + BN_CTX *bnCtx = BN_CTX_new(); + // calculate r + SHA512_CTX ctx; + SHA512_Init(&ctx); + SHA512_Update(&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, + EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key + SHA512_Update(&ctx, buf, len); // data + uint8_t digest[64]; + SHA512_Final(digest, &ctx); + BIGNUM *r = DecodeBN<32>(digest); // DecodeBN<64> (digest); // for test vectors + // calculate R + uint8_t R[EDDSA25519_SIGNATURE_LENGTH / + 2]; // we must use separate buffer because signature might be inside buf + EncodePoint(Normalize(MulB(digest, bnCtx), bnCtx), + R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors + // calculate S + SHA512_Init(&ctx); + SHA512_Update(&ctx, R, EDDSA25519_SIGNATURE_LENGTH / 2); // R + SHA512_Update(&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key + SHA512_Update(&ctx, buf, len); // data + SHA512_Final(digest, &ctx); + BIGNUM *h = DecodeBN<64>(digest); + // S = (r + h*a) % l + BIGNUM *a = DecodeBN(expandedPrivateKey); // left half of expanded key + BN_mod_mul(h, h, a, l, bnCtx); // %l + BN_mod_add(h, h, r, l, bnCtx); // %l + memcpy(signature, R, EDDSA25519_SIGNATURE_LENGTH / 2); + EncodeBN(h, signature + EDDSA25519_SIGNATURE_LENGTH / 2, EDDSA25519_SIGNATURE_LENGTH / 2); // S + BN_free(r); + BN_free(h); + BN_free(a); + BN_CTX_free(bnCtx); + } - void Ed25519::SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, - const uint8_t * buf, size_t len, uint8_t * signature) const - { - BN_CTX * bnCtx = BN_CTX_new (); - // T = 80 random bytes - uint8_t T[80]; - RAND_bytes (T, 80); - // calculate r = H*(T || publickey || data) - SHA512_CTX ctx; - SHA512_Init (&ctx); - SHA512_Update (&ctx, T, 80); - SHA512_Update (&ctx, publicKeyEncoded, 32); - SHA512_Update (&ctx, buf, len); // data - uint8_t digest[64]; - SHA512_Final (digest, &ctx); - BIGNUM * r = DecodeBN<64> (digest); - BN_mod (r, r, l, bnCtx); // % l - EncodeBN (r, digest, 32); - // calculate R - uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf - EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); - // calculate S - SHA512_Init (&ctx); - SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R - SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key - SHA512_Update (&ctx, buf, len); // data - SHA512_Final (digest, &ctx); - BIGNUM * h = DecodeBN<64> (digest); - // S = (r + h*a) % l - BIGNUM * a = DecodeBN (privateKey); - BN_mod_mul (h, h, a, l, bnCtx); // %l - BN_mod_add (h, h, r, l, bnCtx); // %l - memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2); - EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S - BN_free (r); BN_free (h); BN_free (a); - BN_CTX_free (bnCtx); - } + void Ed25519::SignRedDSA(const uint8_t *privateKey, const uint8_t *publicKeyEncoded, + const uint8_t *buf, size_t len, uint8_t *signature) const { + BN_CTX *bnCtx = BN_CTX_new(); + // T = 80 random bytes + uint8_t T[80]; + RAND_bytes(T, 80); + // calculate r = H*(T || publickey || data) + SHA512_CTX ctx; + SHA512_Init(&ctx); + SHA512_Update(&ctx, T, 80); + SHA512_Update(&ctx, publicKeyEncoded, 32); + SHA512_Update(&ctx, buf, len); // data + uint8_t digest[64]; + SHA512_Final(digest, &ctx); + BIGNUM *r = DecodeBN<64>(digest); + BN_mod(r, r, l, bnCtx); // % l + EncodeBN(r, digest, 32); + // calculate R + uint8_t R[EDDSA25519_SIGNATURE_LENGTH / + 2]; // we must use separate buffer because signature might be inside buf + EncodePoint(Normalize(MulB(digest, bnCtx), bnCtx), R); + // calculate S + SHA512_Init(&ctx); + SHA512_Update(&ctx, R, EDDSA25519_SIGNATURE_LENGTH / 2); // R + SHA512_Update(&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key + SHA512_Update(&ctx, buf, len); // data + SHA512_Final(digest, &ctx); + BIGNUM *h = DecodeBN<64>(digest); + // S = (r + h*a) % l + BIGNUM *a = DecodeBN(privateKey); + BN_mod_mul(h, h, a, l, bnCtx); // %l + BN_mod_add(h, h, r, l, bnCtx); // %l + memcpy(signature, R, EDDSA25519_SIGNATURE_LENGTH / 2); + EncodeBN(h, signature + EDDSA25519_SIGNATURE_LENGTH / 2, EDDSA25519_SIGNATURE_LENGTH / 2); // S + BN_free(r); + BN_free(h); + BN_free(a); + BN_CTX_free(bnCtx); + } - EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const - { - // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) - // y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2) - // z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2) - // t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2) - BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new (); + EDDSAPoint Ed25519::Sum(const EDDSAPoint &p1, const EDDSAPoint &p2, BN_CTX *ctx) const { + // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) + // y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2) + // z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2) + // t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2) + BIGNUM *x3 = BN_new(), *y3 = BN_new(), *z3 = BN_new(), *t3 = BN_new(); - BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2 - BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2 + BN_mul(x3, p1.x, p2.x, ctx); // A = x1*x2 + BN_mul(y3, p1.y, p2.y, ctx); // B = y1*y2 - BN_CTX_start (ctx); - BIGNUM * t1 = p1.t, * t2 = p2.t; - if (!t1) { t1 = BN_CTX_get (ctx); BN_mul (t1, p1.x, p1.y, ctx); } - if (!t2) { t2 = BN_CTX_get (ctx); BN_mul (t2, p2.x, p2.y, ctx); } - BN_mul (t3, t1, t2, ctx); - BN_mul (t3, t3, d, ctx); // C = d*t1*t2 + BN_CTX_start(ctx); + BIGNUM *t1 = p1.t, *t2 = p2.t; + if (!t1) { + t1 = BN_CTX_get(ctx); + BN_mul(t1, p1.x, p1.y, ctx); + } + if (!t2) { + t2 = BN_CTX_get(ctx); + BN_mul(t2, p2.x, p2.y, ctx); + } + BN_mul(t3, t1, t2, ctx); + BN_mul(t3, t3, d, ctx); // C = d*t1*t2 - if (p1.z) - { - if (p2.z) - BN_mul (z3, p1.z, p2.z, ctx); // D = z1*z2 - else - BN_copy (z3, p1.z); // D = z1 - } - else - { - if (p2.z) - BN_copy (z3, p2.z); // D = z2 - else - BN_one (z3); // D = 1 - } + if (p1.z) { + if (p2.z) + BN_mul(z3, p1.z, p2.z, ctx); // D = z1*z2 + else + BN_copy(z3, p1.z); // D = z1 + } else { + if (p2.z) + BN_copy(z3, p2.z); // D = z2 + else + BN_one(z3); // D = 1 + } - BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx); - BN_add (E, p1.x, p1.y); - BN_add (F, p2.x, p2.y); - BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2) - BN_sub (E, E, x3); - BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B - BN_sub (F, z3, t3); // F = D - C - BN_add (G, z3, t3); // G = D + C - BN_add (H, y3, x3); // H = B + A + BIGNUM *E = BN_CTX_get(ctx), *F = BN_CTX_get(ctx), *G = BN_CTX_get(ctx), *H = BN_CTX_get(ctx); + BN_add(E, p1.x, p1.y); + BN_add(F, p2.x, p2.y); + BN_mul(E, E, F, ctx); // (x1 + y1)*(x2 + y2) + BN_sub(E, E, x3); + BN_sub(E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B + BN_sub(F, z3, t3); // F = D - C + BN_add(G, z3, t3); // G = D + C + BN_add(H, y3, x3); // H = B + A - BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F - BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H - BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G - BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H + BN_mod_mul(x3, E, F, q, ctx); // x3 = E*F + BN_mod_mul(y3, G, H, q, ctx); // y3 = G*H + BN_mod_mul(z3, F, G, q, ctx); // z3 = F*G + BN_mod_mul(t3, E, H, q, ctx); // t3 = E*H - BN_CTX_end (ctx); + BN_CTX_end(ctx); - return EDDSAPoint {x3, y3, z3, t3}; - } + return EDDSAPoint{x3, y3, z3, t3}; + } - void Ed25519::Double (EDDSAPoint& p, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - BIGNUM * x2 = BN_CTX_get (ctx), * y2 = BN_CTX_get (ctx), * z2 = BN_CTX_get (ctx), * t2 = BN_CTX_get (ctx); + void Ed25519::Double(EDDSAPoint &p, BN_CTX *ctx) const { + BN_CTX_start(ctx); + BIGNUM *x2 = BN_CTX_get(ctx), *y2 = BN_CTX_get(ctx), *z2 = BN_CTX_get(ctx), *t2 = BN_CTX_get(ctx); - BN_sqr (x2, p.x, ctx); // x2 = A = x^2 - BN_sqr (y2, p.y, ctx); // y2 = B = y^2 - if (p.t) - BN_sqr (t2, p.t, ctx); // t2 = t^2 - else - { - BN_mul (t2, p.x, p.y, ctx); // t = x*y - BN_sqr (t2, t2, ctx); // t2 = t^2 - } - BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2 - if (p.z) - BN_sqr (z2, p.z, ctx); // z2 = D = z^2 - else - BN_one (z2); // z2 = 1 + BN_sqr(x2, p.x, ctx); // x2 = A = x^2 + BN_sqr(y2, p.y, ctx); // y2 = B = y^2 + if (p.t) + BN_sqr(t2, p.t, ctx); // t2 = t^2 + else { + BN_mul(t2, p.x, p.y, ctx); // t = x*y + BN_sqr(t2, t2, ctx); // t2 = t^2 + } + BN_mul(t2, t2, d, ctx); // t2 = C = d*t^2 + if (p.z) + BN_sqr(z2, p.z, ctx); // z2 = D = z^2 + else + BN_one(z2); // z2 = 1 - BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx); - // E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy - BN_mul (E, p.x, p.y, ctx); - BN_lshift1 (E, E); // E =2*x*y - BN_sub (F, z2, t2); // F = D - C - BN_add (G, z2, t2); // G = D + C - BN_add (H, y2, x2); // H = B + A + BIGNUM *E = BN_CTX_get(ctx), *F = BN_CTX_get(ctx), *G = BN_CTX_get(ctx), *H = BN_CTX_get(ctx); + // E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy + BN_mul(E, p.x, p.y, ctx); + BN_lshift1(E, E); // E =2*x*y + BN_sub(F, z2, t2); // F = D - C + BN_add(G, z2, t2); // G = D + C + BN_add(H, y2, x2); // H = B + A - BN_mod_mul (p.x, E, F, q, ctx); // x2 = E*F - BN_mod_mul (p.y, G, H, q, ctx); // y2 = G*H - if (!p.z) p.z = BN_new (); - BN_mod_mul (p.z, F, G, q, ctx); // z2 = F*G - if (!p.t) p.t = BN_new (); - BN_mod_mul (p.t, E, H, q, ctx); // t2 = E*H + BN_mod_mul(p.x, E, F, q, ctx); // x2 = E*F + BN_mod_mul(p.y, G, H, q, ctx); // y2 = G*H + if (!p.z) p.z = BN_new(); + BN_mod_mul(p.z, F, G, q, ctx); // z2 = F*G + if (!p.t) p.t = BN_new(); + BN_mod_mul(p.t, E, H, q, ctx); // t2 = E*H - BN_CTX_end (ctx); - } + BN_CTX_end(ctx); + } - EDDSAPoint Ed25519::Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const - { - BIGNUM * zero = BN_new (), * one = BN_new (); - BN_zero (zero); BN_one (one); - EDDSAPoint res {zero, one}; - if (!BN_is_zero (e)) - { - int bitCount = BN_num_bits (e); - for (int i = bitCount - 1; i >= 0; i--) - { - Double (res, ctx); - if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx); - } - } - return res; - } + EDDSAPoint Ed25519::Mul(const EDDSAPoint &p, const BIGNUM *e, BN_CTX *ctx) const { + BIGNUM *zero = BN_new(), *one = BN_new(); + BN_zero(zero); + BN_one(one); + EDDSAPoint res{zero, one}; + if (!BN_is_zero(e)) { + int bitCount = BN_num_bits(e); + for (int i = bitCount - 1; i >= 0; i--) { + Double(res, ctx); + if (BN_is_bit_set(e, i)) res = Sum(res, p, ctx); + } + } + return res; + } - EDDSAPoint Ed25519::MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian - { - BIGNUM * zero = BN_new (), * one = BN_new (); - BN_zero (zero); BN_one (one); - EDDSAPoint res {zero, one}; - bool carry = false; - for (int i = 0; i < 32; i++) - { - uint8_t x = e[i]; - if (carry) - { - if (x < 255) - { - x++; - carry = false; - } - else - x = 0; - } - if (x > 0) - { - if (x <= 128) - res = Sum (res, Bi256[i][x-1], ctx); - else - { - res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x] - carry = true; - } - } - } - if (carry) res = Sum (res, Bi256Carry, ctx); - return res; - } + EDDSAPoint Ed25519::MulB(const uint8_t *e, BN_CTX *ctx) const // B*e, e is 32 bytes Little Endian + { + BIGNUM *zero = BN_new(), *one = BN_new(); + BN_zero(zero); + BN_one(one); + EDDSAPoint res{zero, one}; + bool carry = false; + for (int i = 0; i < 32; i++) { + uint8_t x = e[i]; + if (carry) { + if (x < 255) { + x++; + carry = false; + } else + x = 0; + } + if (x > 0) { + if (x <= 128) + res = Sum(res, Bi256[i][x - 1], ctx); + else { + res = Sum(res, -Bi256[i][255 - x], ctx); // -Bi[256-x] + carry = true; + } + } + } + if (carry) res = Sum(res, Bi256Carry, ctx); + return res; + } - EDDSAPoint Ed25519::Normalize (const EDDSAPoint& p, BN_CTX * ctx) const - { - if (p.z) - { - BIGNUM * x = BN_new (), * y = BN_new (); - BN_mod_inverse (y, p.z, q, ctx); - BN_mod_mul (x, p.x, y, q, ctx); // x = x/z - BN_mod_mul (y, p.y, y, q, ctx); // y = y/z - return EDDSAPoint{x, y}; - } - else - return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)}; - } + EDDSAPoint Ed25519::Normalize(const EDDSAPoint &p, BN_CTX *ctx) const { + if (p.z) { + BIGNUM *x = BN_new(), *y = BN_new(); + BN_mod_inverse(y, p.z, q, ctx); + BN_mod_mul(x, p.x, y, q, ctx); // x = x/z + BN_mod_mul(y, p.y, y, q, ctx); // y = y/z + return EDDSAPoint{x, y}; + } else + return EDDSAPoint{BN_dup(p.x), BN_dup(p.y)}; + } - bool Ed25519::IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - BIGNUM * x2 = BN_CTX_get (ctx), * y2 = BN_CTX_get (ctx), * tmp = BN_CTX_get (ctx); - BN_sqr (x2, p.x, ctx); // x^2 - BN_sqr (y2, p.y, ctx); // y^2 - // y^2 - x^2 - 1 - d*x^2*y^2 - BN_mul (tmp, d, x2, ctx); - BN_mul (tmp, tmp, y2, ctx); - BN_sub (tmp, y2, tmp); - BN_sub (tmp, tmp, x2); - BN_sub_word (tmp, 1); - BN_mod (tmp, tmp, q, ctx); // % q - bool ret = BN_is_zero (tmp); - BN_CTX_end (ctx); - return ret; - } + bool Ed25519::IsOnCurve(const EDDSAPoint &p, BN_CTX *ctx) const { + BN_CTX_start(ctx); + BIGNUM *x2 = BN_CTX_get(ctx), *y2 = BN_CTX_get(ctx), *tmp = BN_CTX_get(ctx); + BN_sqr(x2, p.x, ctx); // x^2 + BN_sqr(y2, p.y, ctx); // y^2 + // y^2 - x^2 - 1 - d*x^2*y^2 + BN_mul(tmp, d, x2, ctx); + BN_mul(tmp, tmp, y2, ctx); + BN_sub(tmp, y2, tmp); + BN_sub(tmp, tmp, x2); + BN_sub_word(tmp, 1); + BN_mod(tmp, tmp, q, ctx); // % q + bool ret = BN_is_zero(tmp); + BN_CTX_end(ctx); + return ret; + } - BIGNUM * Ed25519::RecoverX (const BIGNUM * y, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - BIGNUM * y2 = BN_CTX_get (ctx), * xx = BN_CTX_get (ctx); - BN_sqr (y2, y, ctx); // y^2 - // xx = (y^2 -1)*inv(d*y^2 +1) - BN_mul (xx, d, y2, ctx); - BN_add_word (xx, 1); - BN_mod_inverse (xx, xx, q, ctx); - BN_sub_word (y2, 1); - BN_mul (xx, y2, xx, ctx); - // x = srqt(xx) = xx^(2^252-2) - BIGNUM * x = BN_new (); - BN_mod_exp (x, xx, two_252_2, q, ctx); - // check (x^2 -xx) % q - BN_sqr (y2, x, ctx); - BN_mod_sub (y2, y2, xx, q, ctx); - if (!BN_is_zero (y2)) - BN_mod_mul (x, x, I, q, ctx); - if (BN_is_odd (x)) - BN_sub (x, q, x); - BN_CTX_end (ctx); - return x; - } + BIGNUM *Ed25519::RecoverX(const BIGNUM *y, BN_CTX *ctx) const { + BN_CTX_start(ctx); + BIGNUM *y2 = BN_CTX_get(ctx), *xx = BN_CTX_get(ctx); + BN_sqr(y2, y, ctx); // y^2 + // xx = (y^2 -1)*inv(d*y^2 +1) + BN_mul(xx, d, y2, ctx); + BN_add_word(xx, 1); + BN_mod_inverse(xx, xx, q, ctx); + BN_sub_word(y2, 1); + BN_mul(xx, y2, xx, ctx); + // x = srqt(xx) = xx^(2^252-2) + BIGNUM *x = BN_new(); + BN_mod_exp(x, xx, two_252_2, q, ctx); + // check (x^2 -xx) % q + BN_sqr(y2, x, ctx); + BN_mod_sub(y2, y2, xx, q, ctx); + if (!BN_is_zero(y2)) + BN_mod_mul(x, x, I, q, ctx); + if (BN_is_odd(x)) + BN_sub(x, q, x); + BN_CTX_end(ctx); + return x; + } - EDDSAPoint Ed25519::DecodePoint (const uint8_t * buf, BN_CTX * ctx) const - { - // buf is 32 bytes Little Endian, convert it to Big Endian - uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH]; - for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes - { - buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i]; - buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i]; - } - bool isHighestBitSet = buf1[0] & 0x80; - if (isHighestBitSet) - buf1[0] &= 0x7f; // clear highest bit - BIGNUM * y = BN_new (); - BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y); - BIGNUM * x = RecoverX (y, ctx); - if (BN_is_bit_set (x, 0) != isHighestBitSet) - BN_sub (x, q, x); // x = q - x - BIGNUM * z = BN_new (), * t = BN_new (); - BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t - EDDSAPoint p {x, y, z, t}; - if (!IsOnCurve (p, ctx)) - LogPrint (eLogError, "Decoded point is not on 25519"); - return p; - } + EDDSAPoint Ed25519::DecodePoint(const uint8_t *buf, BN_CTX *ctx) const { + // buf is 32 bytes Little Endian, convert it to Big Endian + uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH]; + for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH / 2; i++) // invert bytes + { + buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1 - i]; + buf1[EDDSA25519_PUBLIC_KEY_LENGTH - 1 - i] = buf[i]; + } + bool isHighestBitSet = buf1[0] & 0x80; + if (isHighestBitSet) + buf1[0] &= 0x7f; // clear highest bit + BIGNUM *y = BN_new(); + BN_bin2bn(buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y); + BIGNUM *x = RecoverX(y, ctx); + if (BN_is_bit_set(x, 0) != isHighestBitSet) + BN_sub(x, q, x); // x = q - x + BIGNUM *z = BN_new(), *t = BN_new(); + BN_one(z); + BN_mod_mul(t, x, y, q, ctx); // pre-calculate t + EDDSAPoint p{x, y, z, t}; + if (!IsOnCurve(p, ctx)) + LogPrint(eLogError, "Decoded point is not on 25519"); + return p; + } - void Ed25519::EncodePoint (const EDDSAPoint& p, uint8_t * buf) const - { - EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH); - if (BN_is_bit_set (p.x, 0)) // highest bit - buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit - } + void Ed25519::EncodePoint(const EDDSAPoint &p, uint8_t *buf) const { + EncodeBN(p.y, buf, EDDSA25519_PUBLIC_KEY_LENGTH); + if (BN_is_bit_set(p.x, 0)) // highest bit + buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit + } - template - BIGNUM * Ed25519::DecodeBN (const uint8_t * buf) const - { - // buf is Little Endian convert it to Big Endian - uint8_t buf1[len]; - for (size_t i = 0; i < len/2; i++) // invert bytes - { - buf1[i] = buf[len -1 - i]; - buf1[len -1 - i] = buf[i]; - } - BIGNUM * res = BN_new (); - BN_bin2bn (buf1, len, res); - return res; - } + template + BIGNUM *Ed25519::DecodeBN(const uint8_t *buf) const { + // buf is Little Endian convert it to Big Endian + uint8_t buf1[len]; + for (size_t i = 0; i < len / 2; i++) // invert bytes + { + buf1[i] = buf[len - 1 - i]; + buf1[len - 1 - i] = buf[i]; + } + BIGNUM *res = BN_new(); + BN_bin2bn(buf1, len, res); + return res; + } - void Ed25519::EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const - { - bn2buf (bn, buf, len); - // To Little Endian - for (size_t i = 0; i < len/2; i++) // invert bytes - { - uint8_t tmp = buf[i]; - buf[i] = buf[len -1 - i]; - buf[len -1 - i] = tmp; - } - } + void Ed25519::EncodeBN(const BIGNUM *bn, uint8_t *buf, size_t len) const { + bn2buf(bn, buf, len); + // To Little Endian + for (size_t i = 0; i < len / 2; i++) // invert bytes + { + uint8_t tmp = buf[i]; + buf[i] = buf[len - 1 - i]; + buf[len - 1 - i] = tmp; + } + } #if !OPENSSL_X25519 - BIGNUM * Ed25519::ScalarMul (const BIGNUM * u, const BIGNUM * k, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - auto x1 = BN_CTX_get (ctx); BN_copy (x1, u); - auto x2 = BN_CTX_get (ctx); BN_one (x2); - auto z2 = BN_CTX_get (ctx); BN_zero (z2); - auto x3 = BN_CTX_get (ctx); BN_copy (x3, u); - auto z3 = BN_CTX_get (ctx); BN_one (z3); - auto c121666 = BN_CTX_get (ctx); BN_set_word (c121666, 121666); - auto tmp0 = BN_CTX_get (ctx); auto tmp1 = BN_CTX_get (ctx); - unsigned int swap = 0; - auto bits = BN_num_bits (k); - while(bits) - { - --bits; - auto k_t = BN_is_bit_set(k, bits) ? 1 : 0; - swap ^= k_t; - if (swap) - { - std::swap (x2, x3); - std::swap (z2, z3); - } - swap = k_t; - BN_mod_sub(tmp0, x3, z3, q, ctx); - BN_mod_sub(tmp1, x2, z2, q, ctx); - BN_mod_add(x2, x2, z2, q, ctx); - BN_mod_add(z2, x3, z3, q, ctx); - BN_mod_mul(z3, tmp0, x2, q, ctx); - BN_mod_mul(z2, z2, tmp1, q, ctx); - BN_mod_sqr(tmp0, tmp1, q, ctx); - BN_mod_sqr(tmp1, x2, q, ctx); - BN_mod_add(x3, z3, z2, q, ctx); - BN_mod_sub(z2, z3, z2, q, ctx); - BN_mod_mul(x2, tmp1, tmp0, q, ctx); - BN_mod_sub(tmp1, tmp1, tmp0, q, ctx); - BN_mod_sqr(z2, z2, q, ctx); - BN_mod_mul(z3, tmp1, c121666, q, ctx); - BN_mod_sqr(x3, x3, q, ctx); - BN_mod_add(tmp0, tmp0, z3, q, ctx); - BN_mod_mul(z3, x1, z2, q, ctx); - BN_mod_mul(z2, tmp1, tmp0, q, ctx); - } - if (swap) - { - std::swap (x2, x3); - std::swap (z2, z3); - } - BN_mod_inverse (z2, z2, q, ctx); - BIGNUM * res = BN_new (); // not from ctx - BN_mod_mul(res, x2, z2, q, ctx); - BN_CTX_end (ctx); - return res; - } - void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const - { - BIGNUM * p1 = DecodeBN<32> (p); - uint8_t k[32]; - memcpy (k, e, 32); - k[0] &= 248; k[31] &= 127; k[31] |= 64; - BIGNUM * n = DecodeBN<32> (k); - BIGNUM * q1 = ScalarMul (p1, n, ctx); - EncodeBN (q1, buf, 32); - BN_free (p1); BN_free (n); BN_free (q1); - } + BIGNUM *Ed25519::ScalarMul(const BIGNUM *u, const BIGNUM *k, BN_CTX *ctx) const { + BN_CTX_start(ctx); + auto x1 = BN_CTX_get(ctx); + BN_copy(x1, u); + auto x2 = BN_CTX_get(ctx); + BN_one(x2); + auto z2 = BN_CTX_get(ctx); + BN_zero(z2); + auto x3 = BN_CTX_get(ctx); + BN_copy(x3, u); + auto z3 = BN_CTX_get(ctx); + BN_one(z3); + auto c121666 = BN_CTX_get(ctx); + BN_set_word(c121666, 121666); + auto tmp0 = BN_CTX_get(ctx); + auto tmp1 = BN_CTX_get(ctx); + unsigned int swap = 0; + auto bits = BN_num_bits(k); + while (bits) { + --bits; + auto k_t = BN_is_bit_set(k, bits) ? 1 : 0; + swap ^= k_t; + if (swap) { + std::swap(x2, x3); + std::swap(z2, z3); + } + swap = k_t; + BN_mod_sub(tmp0, x3, z3, q, ctx); + BN_mod_sub(tmp1, x2, z2, q, ctx); + BN_mod_add(x2, x2, z2, q, ctx); + BN_mod_add(z2, x3, z3, q, ctx); + BN_mod_mul(z3, tmp0, x2, q, ctx); + BN_mod_mul(z2, z2, tmp1, q, ctx); + BN_mod_sqr(tmp0, tmp1, q, ctx); + BN_mod_sqr(tmp1, x2, q, ctx); + BN_mod_add(x3, z3, z2, q, ctx); + BN_mod_sub(z2, z3, z2, q, ctx); + BN_mod_mul(x2, tmp1, tmp0, q, ctx); + BN_mod_sub(tmp1, tmp1, tmp0, q, ctx); + BN_mod_sqr(z2, z2, q, ctx); + BN_mod_mul(z3, tmp1, c121666, q, ctx); + BN_mod_sqr(x3, x3, q, ctx); + BN_mod_add(tmp0, tmp0, z3, q, ctx); + BN_mod_mul(z3, x1, z2, q, ctx); + BN_mod_mul(z2, tmp1, tmp0, q, ctx); + } + if (swap) { + std::swap(x2, x3); + std::swap(z2, z3); + } + BN_mod_inverse(z2, z2, q, ctx); + BIGNUM *res = BN_new(); // not from ctx + BN_mod_mul(res, x2, z2, q, ctx); + BN_CTX_end(ctx); + return res; + } + + void Ed25519::ScalarMul(const uint8_t *p, const uint8_t *e, uint8_t *buf, BN_CTX *ctx) const { + BIGNUM *p1 = DecodeBN<32>(p); + uint8_t k[32]; + memcpy(k, e, 32); + k[0] &= 248; + k[31] &= 127; + k[31] |= 64; + BIGNUM *n = DecodeBN<32>(k); + BIGNUM *q1 = ScalarMul(p1, n, ctx); + EncodeBN(q1, buf, 32); + BN_free(p1); + BN_free(n); + BN_free(q1); + } + + void Ed25519::ScalarMulB(const uint8_t *e, uint8_t *buf, BN_CTX *ctx) const { + BIGNUM *p1 = BN_new(); + BN_set_word(p1, 9); + uint8_t k[32]; + memcpy(k, e, 32); + k[0] &= 248; + k[31] &= 127; + k[31] |= 64; + BIGNUM *n = DecodeBN<32>(k); + BIGNUM *q1 = ScalarMul(p1, n, ctx); + EncodeBN(q1, buf, 32); + BN_free(p1); + BN_free(n); + BN_free(q1); + } - void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const - { - BIGNUM *p1 = BN_new (); BN_set_word (p1, 9); - uint8_t k[32]; - memcpy (k, e, 32); - k[0] &= 248; k[31] &= 127; k[31] |= 64; - BIGNUM * n = DecodeBN<32> (k); - BIGNUM * q1 = ScalarMul (p1, n, ctx); - EncodeBN (q1, buf, 32); - BN_free (p1); BN_free (n); BN_free (q1); - } #endif - void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded) - { - BN_CTX * ctx = BN_CTX_new (); - // calculate alpha = seed mod l - BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian - BN_mod (alpha, alpha, l, ctx); // % l - uint8_t priv[32]; - EncodeBN (alpha, priv, 32); // back to Little Endian - BN_free (alpha); - // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) - auto A1 = Sum (DecodePublicKey (pub, ctx), MulB (priv, ctx), ctx); // pub + B*alpha - EncodePublicKey (A1, blinded, ctx); - BN_CTX_free (ctx); - } + void Ed25519::BlindPublicKey(const uint8_t *pub, const uint8_t *seed, uint8_t *blinded) { + BN_CTX *ctx = BN_CTX_new(); + // calculate alpha = seed mod l + BIGNUM *alpha = DecodeBN<64>(seed); // seed is in Little Endian + BN_mod(alpha, alpha, l, ctx); // % l + uint8_t priv[32]; + EncodeBN(alpha, priv, 32); // back to Little Endian + BN_free(alpha); + // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) + auto A1 = Sum(DecodePublicKey(pub, ctx), MulB(priv, ctx), ctx); // pub + B*alpha + EncodePublicKey(A1, blinded, ctx); + BN_CTX_free(ctx); + } - void Ed25519::BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub) - { - BN_CTX * ctx = BN_CTX_new (); - // calculate alpha = seed mod l - BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian - BN_mod (alpha, alpha, l, ctx); // % l - BIGNUM * p = DecodeBN<32> (priv); // priv is in Little Endian - BN_add (alpha, alpha, p); // alpha = alpha + priv - // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L - BN_mod (alpha, alpha, l, ctx); // % l - EncodeBN (alpha, blindedPriv, 32); - // A' = DERIVE_PUBLIC(a') - auto A1 = MulB (blindedPriv, ctx); - EncodePublicKey (A1, blindedPub, ctx); - BN_free (alpha); BN_free (p); - BN_CTX_free (ctx); - } + void + Ed25519::BlindPrivateKey(const uint8_t *priv, const uint8_t *seed, uint8_t *blindedPriv, uint8_t *blindedPub) { + BN_CTX *ctx = BN_CTX_new(); + // calculate alpha = seed mod l + BIGNUM *alpha = DecodeBN<64>(seed); // seed is in Little Endian + BN_mod(alpha, alpha, l, ctx); // % l + BIGNUM *p = DecodeBN<32>(priv); // priv is in Little Endian + BN_add(alpha, alpha, p); // alpha = alpha + priv + // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L + BN_mod(alpha, alpha, l, ctx); // % l + EncodeBN(alpha, blindedPriv, 32); + // A' = DERIVE_PUBLIC(a') + auto A1 = MulB(blindedPriv, ctx); + EncodePublicKey(A1, blindedPub, ctx); + BN_free(alpha); + BN_free(p); + BN_CTX_free(ctx); + } - void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey) - { - SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey); - expandedKey[0] &= 0xF8; // drop last 3 bits - expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits - expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit - } + void Ed25519::ExpandPrivateKey(const uint8_t *key, uint8_t *expandedKey) { + SHA512(key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey); + expandedKey[0] &= 0xF8; // drop last 3 bits + expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits + expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit + } - void Ed25519::CreateRedDSAPrivateKey (uint8_t * priv) - { - uint8_t seed[32]; - RAND_bytes (seed, 32); - BIGNUM * p = DecodeBN<32> (seed); - BN_CTX * ctx = BN_CTX_new (); - BN_mod (p, p, l, ctx); // % l - EncodeBN (p, priv, 32); - BN_CTX_free (ctx); - BN_free (p); - } + void Ed25519::CreateRedDSAPrivateKey(uint8_t *priv) { + uint8_t seed[32]; + RAND_bytes(seed, 32); + BIGNUM *p = DecodeBN<32>(seed); + BN_CTX *ctx = BN_CTX_new(); + BN_mod(p, p, l, ctx); // % l + EncodeBN(p, priv, 32); + BN_CTX_free(ctx); + BN_free(p); + } - static std::unique_ptr g_Ed25519; - std::unique_ptr& GetEd25519 () - { - if (!g_Ed25519) - { - auto c = new Ed25519(); - if (!g_Ed25519) // make sure it was not created already - g_Ed25519.reset (c); - else - delete c; - } - return g_Ed25519; - } -} + static std::unique_ptr g_Ed25519; + + std::unique_ptr &GetEd25519() { + if (!g_Ed25519) { + auto c = new Ed25519(); + if (!g_Ed25519) // make sure it was not created already + g_Ed25519.reset(c); + else + delete c; + } + return g_Ed25519; + } + } } diff --git a/libi2pd/Ed25519.h b/libi2pd/Ed25519.h index 470d802f..51ebcb78 100644 --- a/libi2pd/Ed25519.h +++ b/libi2pd/Ed25519.h @@ -13,127 +13,168 @@ #include #include "Crypto.h" -namespace i2p -{ -namespace crypto -{ - struct EDDSAPoint - { - BIGNUM * x {nullptr}; - BIGNUM * y {nullptr}; - BIGNUM * z {nullptr}; - BIGNUM * t {nullptr}; // projective coordinates +namespace i2p { + namespace crypto { + struct EDDSAPoint { + BIGNUM *x{nullptr}; + BIGNUM *y{nullptr}; + BIGNUM *z{nullptr}; + BIGNUM *t{nullptr}; // projective coordinates - 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() {} - EDDSAPoint& operator=(EDDSAPoint&& 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(const EDDSAPoint &other) { *this = other; } - EDDSAPoint& operator=(const EDDSAPoint& 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(EDDSAPoint &&other) { *this = std::move(other); } - EDDSAPoint operator-() const - { - 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}; - } - }; + EDDSAPoint(BIGNUM *x1, BIGNUM *y1, BIGNUM *z1 = nullptr, BIGNUM *t1 = nullptr) + : x(x1), y(y1), z(z1), t(t1) {} - const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; - const size_t EDDSA25519_SIGNATURE_LENGTH = 64; - const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; - class Ed25519 - { - public: + ~EDDSAPoint() { + BN_free(x); + BN_free(y); + BN_free(z); + BN_free(t); + } - Ed25519 (); - Ed25519 (const Ed25519& other); - ~Ed25519 (); + EDDSAPoint &operator=(EDDSAPoint &&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 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 - 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 - 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 + EDDSAPoint &operator=(const EDDSAPoint &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; + } - bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const; - void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; - void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; + EDDSAPoint operator-() const { + 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}; + } + }; - 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 + const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; + 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; - 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; + Ed25519(); - 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; + Ed25519(const Ed25519 &other); - template - BIGNUM * DecodeBN (const uint8_t * buf) const; - void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const; + ~Ed25519(); + + 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 - // 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 - 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; - // 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] - }; + bool Verify(const EDDSAPoint &publicKey, const uint8_t *digest, const uint8_t *signature) const; - std::unique_ptr& 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 + 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 &GetEd25519(); + + } } #endif diff --git a/libi2pd/Elligator.cpp b/libi2pd/Elligator.cpp index 25e09893..28395321 100644 --- a/libi2pd/Elligator.cpp +++ b/libi2pd/Elligator.cpp @@ -10,205 +10,209 @@ #include "Crypto.h" #include "Elligator.h" -namespace i2p -{ -namespace crypto -{ +namespace i2p { + namespace crypto { - Elligator2::Elligator2 () - { - // TODO: share with Ed22519 - p = BN_new (); - // 2^255-19 - BN_set_bit (p, 255); // 2^255 - BN_sub_word (p, 19); - p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8 - 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 + Elligator2::Elligator2() { + // TODO: share with Ed22519 + p = BN_new(); + // 2^255-19 + BN_set_bit(p, 255); // 2^255 + BN_sub_word(p, 19); + p38 = BN_dup(p); + BN_add_word(p38, 3); + BN_div_word(p38, 8); // (p+3)/8 + 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); - nA = BN_new (); BN_sub (nA, p, A); + A = BN_new(); + BN_set_word(A, 486662); + nA = BN_new(); + BN_sub(nA, p, A); - BN_CTX * ctx = BN_CTX_new (); - // calculate sqrt(-1) - sqrtn1 = BN_new (); - BN_set_word (sqrtn1, 2); - BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4 + BN_CTX *ctx = BN_CTX_new(); + // calculate sqrt(-1) + sqrtn1 = BN_new(); + BN_set_word(sqrtn1, 2); + BN_mod_exp(sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4 - u = BN_new (); BN_set_word (u, 2); - iu = BN_new (); BN_mod_inverse (iu, u, p, ctx); + u = BN_new(); + 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 () - { - BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14); - BN_free (sqrtn1); BN_free (A); BN_free (nA); - BN_free (u); BN_free (iu); - } + Elligator2::~Elligator2() { + BN_free(p); + BN_free(p38); + BN_free(p12); + BN_free(p14); + BN_free(sqrtn1); + BN_free(A); + BN_free(nA); + BN_free(u); + BN_free(iu); + } - bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const - { - bool ret = true; - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); + bool Elligator2::Encode(const uint8_t *key, uint8_t *encoded, bool highY, bool random) const { + bool ret = true; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); - uint8_t key1[32]; - for (size_t i = 0; i < 16; i++) // from Little Endian - { - key1[i] = key[31 - i]; - key1[31 - i] = key[i]; - } + uint8_t key1[32]; + for (size_t i = 0; i < 16; i++) // from Little Endian + { + key1[i] = key[31 - i]; + key1[31 - i] = key[i]; + } - BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x); - BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A - BN_sub (xA, p, xA); // p - (x + A) + BIGNUM *x = BN_CTX_get(ctx); + BN_bin2bn(key1, 32, x); + 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 - BN_mod_mul (uxxA, u, x, p, ctx); - BN_mod_mul (uxxA, uxxA, xA, p, ctx); + BIGNUM *uxxA = BN_CTX_get(ctx); // u*x*xA + BN_mod_mul(uxxA, u, x, p, ctx); + BN_mod_mul(uxxA, uxxA, xA, p, ctx); - if (Legendre (uxxA, ctx) != -1) - { - uint8_t randByte = 0; // random highest bits and high y - if (random) - { - RAND_bytes (&randByte, 1); - highY = randByte & 0x01; - } + if (Legendre(uxxA, ctx) != -1) { + uint8_t randByte = 0; // random highest bits and high y + if (random) { + RAND_bytes(&randByte, 1); + highY = randByte & 0x01; + } - BIGNUM * r = BN_CTX_get (ctx); - if (highY) - { - BN_mod_inverse (r, x, p, ctx); - BN_mod_mul (r, r, xA, p, ctx); - } - else - { - BN_mod_inverse (r, xA, p, ctx); - BN_mod_mul (r, r, x, p, ctx); - } - BN_mod_mul (r, r, iu, p, ctx); + BIGNUM *r = BN_CTX_get(ctx); + if (highY) { + BN_mod_inverse(r, x, p, ctx); + BN_mod_mul(r, r, xA, p, ctx); + } else { + BN_mod_inverse(r, xA, p, ctx); + BN_mod_mul(r, r, x, p, ctx); + } + BN_mod_mul(r, r, iu, p, ctx); - SquareRoot (r, r, ctx); - bn2buf (r, encoded, 32); + SquareRoot(r, r, ctx); + bn2buf(r, encoded, 32); - if (random) - encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte - for (size_t i = 0; i < 16; i++) // To Little Endian - { - uint8_t tmp = encoded[i]; - encoded[i] = encoded[31 - i]; - encoded[31 - i] = tmp; - } - } - else - ret = false; + if (random) + encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte + for (size_t i = 0; i < 16; i++) // To Little Endian + { + uint8_t tmp = encoded[i]; + encoded[i] = encoded[31 - i]; + encoded[31 - i] = tmp; + } + } else + ret = false; - BN_CTX_end (ctx); - BN_CTX_free (ctx); - return ret; - } + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; + } - bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const - { - bool ret = true; - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); + bool Elligator2::Decode(const uint8_t *encoded, uint8_t *key) const { + bool ret = true; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); - uint8_t encoded1[32]; - for (size_t i = 0; i < 16; i++) // from Little Endian - { - encoded1[i] = encoded[31 - i]; - encoded1[31 - i] = encoded[i]; - } - encoded1[0] &= 0x3F; // drop two highest bits + uint8_t encoded1[32]; + for (size_t i = 0; i < 16; i++) // from Little Endian + { + encoded1[i] = encoded[31 - i]; + encoded1[31 - i] = encoded[i]; + } + encoded1[0] &= 0x3F; // drop two highest bits - 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 - { - // v = -A/(1+u*r^2) - BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx); - BN_mod_mul (v, v, u, p, ctx); - BN_add_word (v, 1); - BN_mod_inverse (v, v, p, ctx); - BN_mod_mul (v, v, nA, p, ctx); + if (BN_cmp(r, p12) <= 0) // r < (p-1)/2 + { + // v = -A/(1+u*r^2) + BIGNUM *v = BN_CTX_get(ctx); + BN_mod_sqr(v, r, p, ctx); + BN_mod_mul(v, v, u, p, ctx); + BN_add_word(v, 1); + BN_mod_inverse(v, v, p, ctx); + BN_mod_mul(v, v, nA, p, ctx); - BIGNUM * vpA = BN_CTX_get (ctx); - BN_add (vpA, v, A); // v + A - // t = v^3+A*v^2+v = v^2*(v+A)+v - BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx); - BN_mod_mul (t, t, vpA, p, ctx); - BN_mod_add (t, t, v, p, ctx); + BIGNUM *vpA = BN_CTX_get(ctx); + BN_add(vpA, v, A); // v + A + // t = v^3+A*v^2+v = v^2*(v+A)+v + BIGNUM *t = BN_CTX_get(ctx); + BN_mod_sqr(t, v, p, ctx); + BN_mod_mul(t, t, vpA, p, ctx); + BN_mod_add(t, t, v, p, ctx); - int legendre = Legendre (t, ctx); - BIGNUM * x = BN_CTX_get (ctx); - if (legendre == 1) - BN_copy (x, v); - else - { - BN_sub (x, p, v); - BN_mod_sub (x, x, A, p, ctx); - } + int legendre = Legendre(t, ctx); + BIGNUM *x = BN_CTX_get(ctx); + if (legendre == 1) + BN_copy(x, v); + else { + BN_sub(x, p, v); + BN_mod_sub(x, x, A, p, ctx); + } - bn2buf (x, key, 32); - for (size_t i = 0; i < 16; i++) // To Little Endian - { - uint8_t tmp = key[i]; - key[i] = key[31 - i]; - key[31 - i] = tmp; - } - } - else - ret = false; + bn2buf(x, key, 32); + for (size_t i = 0; i < 16; i++) // To Little Endian + { + uint8_t tmp = key[i]; + key[i] = key[31 - i]; + key[31 - i] = tmp; + } + } else + ret = false; - BN_CTX_end (ctx); - BN_CTX_free (ctx); + BN_CTX_end(ctx); + BN_CTX_free(ctx); - return ret; - } + return ret; + } - void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const - { - BIGNUM * t = BN_CTX_get (ctx); - 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_add_word (t, 1); + void Elligator2::SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const { + BIGNUM *t = BN_CTX_get(ctx); + 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_add_word(t, 1); - if (!BN_cmp (t, p)) - BN_mod_mul (r, r, sqrtn1, p, ctx); + if (!BN_cmp(t, p)) + BN_mod_mul(r, r, sqrtn1, p, ctx); - if (BN_cmp (r, p12) > 0) // r > (p-1)/2 - BN_sub (r, p, r); - } + if (BN_cmp(r, p12) > 0) // r > (p-1)/2 + BN_sub(r, p, r); + } - int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const - { - // assume a < p, so don't check for a % p = 0, but a = 0 only - if (BN_is_zero(a)) return 0; - BIGNUM * r = BN_CTX_get (ctx); - BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p - if (BN_is_word(r, 1)) - return 1; - else if (BN_is_zero(r)) - return 0; - return -1; - } + int Elligator2::Legendre(const BIGNUM *a, BN_CTX *ctx) const { + // assume a < p, so don't check for a % p = 0, but a = 0 only + if (BN_is_zero(a)) return 0; + BIGNUM *r = BN_CTX_get(ctx); + BN_mod_exp(r, a, p12, p, ctx); // r = a^((p-1)/2) mod p + if (BN_is_word(r, 1)) + return 1; + else if (BN_is_zero(r)) + return 0; + return -1; + } - static std::unique_ptr g_Elligator; - std::unique_ptr& GetElligator () - { - if (!g_Elligator) - { - auto el = new Elligator2(); - if (!g_Elligator) // make sure it was not created already - g_Elligator.reset (el); - else - delete el; - } - return g_Elligator; - } -} + static std::unique_ptr g_Elligator; + + std::unique_ptr &GetElligator() { + if (!g_Elligator) { + auto el = new Elligator2(); + if (!g_Elligator) // make sure it was not created already + g_Elligator.reset(el); + else + delete el; + } + return g_Elligator; + } + } } diff --git a/libi2pd/Elligator.h b/libi2pd/Elligator.h index eacb03cd..06b0c7da 100644 --- a/libi2pd/Elligator.h +++ b/libi2pd/Elligator.h @@ -13,33 +13,33 @@ #include #include -namespace i2p -{ -namespace crypto -{ +namespace i2p { + namespace crypto { - class Elligator2 - { - public: + class Elligator2 { + public: - Elligator2 (); - ~Elligator2 (); + Elligator2(); - bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const; - bool Decode (const uint8_t * encoded, uint8_t * key) const; + ~Elligator2(); - 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; - int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p + bool Decode(const uint8_t *encoded, uint8_t *key) const; - 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& 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 &GetElligator(); + } } #endif diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 7334550f..458d9ae4 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -21,273 +21,267 @@ #include "Garlic.h" namespace i2p { -namespace fs { - std::string appName = "i2pd"; - std::string dataDir = ""; - std::string certsDir = ""; + namespace fs { + std::string appName = "i2pd"; + std::string dataDir = ""; + std::string certsDir = ""; #ifdef _WIN32 - std::string dirSep = "\\"; + std::string dirSep = "\\"; #else - std::string dirSep = "/"; + std::string dirSep = "/"; #endif - const std::string & GetAppName () { - return appName; - } + const std::string &GetAppName() { + return appName; + } - void SetAppName (const std::string& name) { - appName = name; - } + void SetAppName(const std::string &name) { + appName = name; + } - const std::string & GetDataDir () { - return dataDir; - } + const std::string &GetDataDir() { + return dataDir; + } - const std::string & GetCertsDir () { - return certsDir; - } + const std::string &GetCertsDir() { + return certsDir; + } - const std::string GetUTF8DataDir () { + const std::string GetUTF8DataDir() { #ifdef _WIN32 - boost::filesystem::wpath path (dataDir); - auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16() ) ); // convert path to UTF-8 - auto dataDirUTF8 = path.string(); - boost::filesystem::path::imbue(loc); // Return locale settings back - return dataDirUTF8; + boost::filesystem::wpath path (dataDir); + auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16() ) ); // convert path to UTF-8 + auto dataDirUTF8 = path.string(); + boost::filesystem::path::imbue(loc); // Return locale settings back + return dataDirUTF8; #else - return dataDir; // linux, osx, android uses UTF-8 by default + return dataDir; // linux, osx, android uses UTF-8 by default #endif - } + } - void DetectDataDir(const std::string & cmdline_param, bool isService) { - // with 'datadir' option - if (cmdline_param != "") { - dataDir = cmdline_param; - return; - } + void DetectDataDir(const std::string &cmdline_param, bool isService) { + // with 'datadir' option + if (cmdline_param != "") { + dataDir = cmdline_param; + return; + } #if !defined(MAC_OSX) && !defined(ANDROID) - // with 'service' option - if (isService) { + // with 'service' option + if (isService) { #ifdef _WIN32 - wchar_t commonAppData[MAX_PATH]; - if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK) - { + wchar_t commonAppData[MAX_PATH]; + if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK) + { #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 - fprintf(stderr, "Error: Unable to get common AppData path!"); + fprintf(stderr, "Error: Unable to get common AppData path!"); #endif - exit(1); - } - else - { - dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; - } + exit(1); + } + else + { + dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; + } #else - dataDir = "/var/lib/" + appName; + dataDir = "/var/lib/" + appName; #endif - return; - } + return; + } #endif - // detect directory as usual + // detect directory as usual #ifdef _WIN32 - wchar_t localAppData[MAX_PATH]; + wchar_t localAppData[MAX_PATH]; - // check executable directory first - if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH)) - { + // check executable directory first + if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH)) + { #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 - fprintf(stderr, "Error: Unable to get application path!"); + fprintf(stderr, "Error: Unable to get application path!"); #endif - exit(1); - } - else - { - auto execPath = boost::filesystem::wpath(localAppData).parent_path(); + exit(1); + } + else + { + auto execPath = boost::filesystem::wpath(localAppData).parent_path(); - // if config file exists in .exe's folder use it - if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string - { - dataDir = execPath.string (); - } else // otherwise %appdata% - { - if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) - { + // if config file exists in .exe's folder use it + if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string + { + dataDir = execPath.string (); + } else // otherwise %appdata% + { + if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) + { #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 - fprintf(stderr, "Error: Unable to get AppData path!"); + fprintf(stderr, "Error: Unable to get AppData path!"); #endif - exit(1); - } - else - { - dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; - } - } - } - return; + exit(1); + } + else + { + dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; + } + } + } + return; #elif defined(MAC_OSX) - char *home = getenv("HOME"); - dataDir = (home != NULL && strlen(home) > 0) ? home : ""; - dataDir += "/Library/Application Support/" + appName; - return; + char *home = getenv("HOME"); + dataDir = (home != NULL && strlen(home) > 0) ? home : ""; + dataDir += "/Library/Application Support/" + appName; + return; #else /* other unix */ #if defined(ANDROID) - const char * ext = getenv("EXTERNAL_STORAGE"); - if (!ext) ext = "/sdcard"; - if (boost::filesystem::exists(ext)) - { - dataDir = std::string (ext) + "/" + appName; - return; - } + const char * ext = getenv("EXTERNAL_STORAGE"); + if (!ext) ext = "/sdcard"; + if (boost::filesystem::exists(ext)) + { + dataDir = std::string (ext) + "/" + appName; + return; + } #endif // ANDROID - // use /home/user/.i2pd or /tmp/i2pd - char *home = getenv("HOME"); - if (home != NULL && strlen(home) > 0) { - dataDir = std::string(home) + "/." + appName; - } else { - dataDir = "/tmp/" + appName; - } - return; + // use /home/user/.i2pd or /tmp/i2pd + char *home = getenv("HOME"); + if (home != NULL && strlen(home) > 0) { + dataDir = std::string(home) + "/." + appName; + } else { + dataDir = "/tmp/" + appName; + } + return; #endif - } + } - void SetCertsDir(const std::string & cmdline_certsdir) { - if (cmdline_certsdir != "") - { - if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/') - certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash - else - certsDir = cmdline_certsdir; - } - else - { - certsDir = i2p::fs::DataDirPath("certificates"); - } - return; - } + void SetCertsDir(const std::string &cmdline_certsdir) { + if (cmdline_certsdir != "") { + if (cmdline_certsdir[cmdline_certsdir.length() - 1] == '/') + certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size() - 1); // strip trailing slash + else + certsDir = cmdline_certsdir; + } else { + certsDir = i2p::fs::DataDirPath("certificates"); + } + return; + } - bool Init() { - if (!boost::filesystem::exists(dataDir)) - boost::filesystem::create_directory(dataDir); + bool Init() { + if (!boost::filesystem::exists(dataDir)) + boost::filesystem::create_directory(dataDir); - std::string destinations = DataDirPath("destinations"); - if (!boost::filesystem::exists(destinations)) - boost::filesystem::create_directory(destinations); + std::string destinations = DataDirPath("destinations"); + if (!boost::filesystem::exists(destinations)) + boost::filesystem::create_directory(destinations); - std::string tags = DataDirPath("tags"); - if (!boost::filesystem::exists(tags)) - boost::filesystem::create_directory(tags); - else - i2p::garlic::CleanUpTagsFiles (); + std::string tags = DataDirPath("tags"); + if (!boost::filesystem::exists(tags)) + boost::filesystem::create_directory(tags); + else + i2p::garlic::CleanUpTagsFiles(); - return true; - } + return true; + } - bool ReadDir(const std::string & path, std::vector & files) { - if (!boost::filesystem::exists(path)) - return false; - boost::filesystem::directory_iterator it(path); - boost::filesystem::directory_iterator end; + bool ReadDir(const std::string &path, std::vector &files) { + if (!boost::filesystem::exists(path)) + return false; + boost::filesystem::directory_iterator it(path); + boost::filesystem::directory_iterator end; - for ( ; it != end; it++) { - if (!boost::filesystem::is_regular_file(it->status())) - continue; - files.push_back(it->path().string()); - } + for (; it != end; it++) { + if (!boost::filesystem::is_regular_file(it->status())) + continue; + files.push_back(it->path().string()); + } - return true; - } + return true; + } - bool Exists(const std::string & path) { - return boost::filesystem::exists(path); - } + bool Exists(const std::string &path) { + return boost::filesystem::exists(path); + } - uint32_t GetLastUpdateTime (const std::string & path) - { - if (!boost::filesystem::exists(path)) - return 0; - boost::system::error_code ec; - auto t = boost::filesystem::last_write_time (path, ec); - return ec ? 0 : t; - } + uint32_t GetLastUpdateTime(const std::string &path) { + if (!boost::filesystem::exists(path)) + return 0; + boost::system::error_code ec; + auto t = boost::filesystem::last_write_time(path, ec); + return ec ? 0 : t; + } - bool Remove(const std::string & path) { - if (!boost::filesystem::exists(path)) - return false; - return boost::filesystem::remove(path); - } + bool Remove(const std::string &path) { + if (!boost::filesystem::exists(path)) + return false; + return boost::filesystem::remove(path); + } - bool CreateDirectory (const std::string& path) - { - if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path))) - return true; - return boost::filesystem::create_directory(path); - } + bool CreateDirectory(const std::string &path) { + if (boost::filesystem::exists(path) && boost::filesystem::is_directory(boost::filesystem::status(path))) + return true; + return boost::filesystem::create_directory(path); + } - void HashedStorage::SetPlace(const std::string &path) { - root = path + i2p::fs::dirSep + name; - } + void HashedStorage::SetPlace(const std::string &path) { + root = path + i2p::fs::dirSep + name; + } - bool HashedStorage::Init(const char * chars, size_t count) { - if (!boost::filesystem::exists(root)) { - boost::filesystem::create_directories(root); - } + bool HashedStorage::Init(const char *chars, size_t count) { + if (!boost::filesystem::exists(root)) { + boost::filesystem::create_directories(root); + } - for (size_t i = 0; i < count; i++) { - auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; - if (boost::filesystem::exists(p)) - continue; - if (boost::filesystem::create_directory(p)) - continue; /* ^ throws exception on failure */ - return false; - } - return true; - } + for (size_t i = 0; i < count; i++) { + auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; + if (boost::filesystem::exists(p)) + continue; + if (boost::filesystem::create_directory(p)) + continue; /* ^ throws exception on failure */ + return false; + } + return true; + } - std::string HashedStorage::Path(const std::string & ident) const { - std::string safe_ident = ident; - std::replace(safe_ident.begin(), safe_ident.end(), '/', '-'); - std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-'); + std::string HashedStorage::Path(const std::string &ident) const { + std::string safe_ident = ident; + std::replace(safe_ident.begin(), safe_ident.end(), '/', '-'); + std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-'); - std::stringstream t(""); - t << this->root << i2p::fs::dirSep; - t << prefix1 << safe_ident[0] << i2p::fs::dirSep; - t << prefix2 << safe_ident << "." << suffix; + std::stringstream t(""); + t << this->root << i2p::fs::dirSep; + t << prefix1 << safe_ident[0] << i2p::fs::dirSep; + t << prefix2 << safe_ident << "." << suffix; - return t.str(); - } + return t.str(); + } - void HashedStorage::Remove(const std::string & ident) { - std::string path = Path(ident); - if (!boost::filesystem::exists(path)) - return; - boost::filesystem::remove(path); - } + void HashedStorage::Remove(const std::string &ident) { + std::string path = Path(ident); + if (!boost::filesystem::exists(path)) + return; + boost::filesystem::remove(path); + } - void HashedStorage::Traverse(std::vector & files) { - Iterate([&files] (const std::string & fname) { - files.push_back(fname); - }); - } + void HashedStorage::Traverse(std::vector &files) { + Iterate([&files](const std::string &fname) { + files.push_back(fname); + }); + } - void HashedStorage::Iterate(FilenameVisitor v) - { - boost::filesystem::path p(root); - boost::filesystem::recursive_directory_iterator it(p); - boost::filesystem::recursive_directory_iterator end; + void HashedStorage::Iterate(FilenameVisitor v) { + boost::filesystem::path p(root); + boost::filesystem::recursive_directory_iterator it(p); + boost::filesystem::recursive_directory_iterator end; - for ( ; it != end; it++) { - if (!boost::filesystem::is_regular_file( it->status() )) - continue; - const std::string & t = it->path().string(); - v(t); - } - } -} // fs + for (; it != end; it++) { + if (!boost::filesystem::is_regular_file(it->status())) + continue; + const std::string &t = it->path().string(); + v(t); + } + } + } // fs } // i2p diff --git a/libi2pd/FS.h b/libi2pd/FS.h index 7911c6a0..f3255931 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -16,168 +16,175 @@ #include namespace i2p { -namespace fs { - extern std::string dirSep; + namespace fs { + extern std::string dirSep; - /** - * @brief Class to work with NetDb & Router profiles - * - * Usage: - * - * const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; - * auto h = HashedStorage("name", "y", "z-", ".txt"); - * h.SetPlace("/tmp/hs-test"); - * h.GetName() -> gives "name" - * h.GetRoot() -> gives "/tmp/hs-test/name" - * h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet - * h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt - * h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists - * std::vector files; - * h.Traverse(files); <- finds all files in storage and saves in given vector - */ - class HashedStorage - { - protected: + /** + * @brief Class to work with NetDb & Router profiles + * + * Usage: + * + * const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; + * auto h = HashedStorage("name", "y", "z-", ".txt"); + * h.SetPlace("/tmp/hs-test"); + * h.GetName() -> gives "name" + * h.GetRoot() -> gives "/tmp/hs-test/name" + * h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet + * h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt + * h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists + * std::vector files; + * h.Traverse(files); <- finds all files in storage and saves in given vector + */ + class HashedStorage { + protected: - std::string root; /**< path to storage with it's name included */ - std::string name; /**< name of the storage */ - std::string prefix1; /**< hashed directory prefix */ - std::string prefix2; /**< prefix of file in storage */ - std::string suffix; /**< suffix of file in storage (extension) */ + std::string root; /**< path to storage with it's name included */ + std::string name; /**< name of the storage */ + std::string prefix1; /**< hashed directory prefix */ + std::string prefix2; /**< prefix of file in storage */ + std::string suffix; /**< suffix of file in storage (extension) */ - public: + public: - typedef std::function FilenameVisitor; - HashedStorage(const char *n, const char *p1, const char *p2, const char *s): - name(n), prefix1(p1), prefix2(p2), suffix(s) {}; + typedef std::function FilenameVisitor; - /** create subdirs in storage */ - bool Init(const char* chars, size_t cnt); - const std::string & GetRoot() const { return root; } - const std::string & GetName() const { return name; } - /** set directory where to place storage directory */ - void SetPlace(const std::string & path); - /** path to file with given ident */ - std::string Path(const std::string & ident) const; - /** remove file by ident */ - void Remove(const std::string & ident); - /** find all files in storage and store list in provided vector */ - void Traverse(std::vector & files); - /** visit every file in this storage with a visitor */ - void Iterate(FilenameVisitor v); - }; + HashedStorage(const char *n, const char *p1, const char *p2, const char *s) : + name(n), prefix1(p1), prefix2(p2), suffix(s) {}; - /** @brief Returns current application name, default 'i2pd' */ - const std::string & GetAppName (); - /** @brief Set application name, affects autodetection of datadir */ - void SetAppName (const std::string& name); + /** create subdirs in storage */ + bool Init(const char *chars, size_t cnt); - /** @brief Returns datadir path */ - const std::string & GetDataDir(); + const std::string &GetRoot() const { return root; } - /** @brief Returns certsdir path */ - const std::string & GetCertsDir(); + const std::string &GetName() const { return name; } - /** @brief Returns datadir path in UTF-8 encoding */ - const std::string GetUTF8DataDir(); + /** set directory where to place storage directory */ + void SetPlace(const std::string &path); - /** - * @brief Set datadir either from cmdline option or using autodetection - * @param cmdline_param Value of cmdline parameter --datadir= - * @param isService Value of cmdline parameter --service - * - * Examples of autodetected paths: - * - * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\ - * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\ - * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ - * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ - */ - void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); + /** path to file with given ident */ + std::string Path(const std::string &ident) const; - /** - * @brief Set certsdir either from cmdline option or using autodetection - * @param cmdline_param Value of cmdline parameter --certsdir= - * - * 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); + /** remove file by ident */ + void Remove(const std::string &ident); - /** - * @brief Create subdirectories inside datadir - */ - bool Init(); + /** find all files in storage and store list in provided vector */ + void Traverse(std::vector &files); - /** - * @brief Get list of files in directory - * @param path Path to directory - * @param files Vector to store found files - * @return true on success and false if directory not exists - */ - bool ReadDir(const std::string & path, std::vector & files); + /** visit every file in this storage with a visitor */ + void Iterate(FilenameVisitor v); + }; - /** - * @brief Remove file with given path - * @param path Absolute path to file - * @return true on success, false if file not exists, throws exception on error - */ - bool Remove(const std::string & path); + /** @brief Returns current application name, default 'i2pd' */ + const std::string &GetAppName(); - /** - * @brief Check existence of file - * @param path Absolute path to file - * @return true if file exists, false otherwise - */ - bool Exists(const std::string & path); + /** @brief Set application name, affects autodetection of datadir */ + void SetAppName(const std::string &name); - 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 - void _ExpandPath(std::stringstream & path, T c) { - path << i2p::fs::dirSep << c; - } + /** @brief Returns datadir path in UTF-8 encoding */ + const std::string GetUTF8DataDir(); - template - void _ExpandPath(std::stringstream & path, T c, Other ... other) { - _ExpandPath(path, c); - _ExpandPath(path, other ...); - } + /** + * @brief Set datadir either from cmdline option or using autodetection + * @param cmdline_param Value of cmdline parameter --datadir= + * @param isService Value of cmdline parameter --service + * + * Examples of autodetected paths: + * + * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\ + * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\ + * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ + * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ + */ + void DetectDataDir(const std::string &cmdline_datadir, bool isService = false); - /** - * @brief Get path relative to datadir - * - * Examples (with datadir = "/tmp/i2pd"): - * - * i2p::fs::Path("test") -> '/tmp/i2pd/test' - * i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt' - */ - template - std::string DataDirPath(Other ... components) { - std::stringstream s(""); - s << i2p::fs::GetDataDir(); - _ExpandPath(s, components ...); + /** + * @brief Set certsdir either from cmdline option or using autodetection + * @param cmdline_param Value of cmdline parameter --certsdir= + * + * 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); - return s.str(); - } + /** + * @brief Create subdirectories inside datadir + */ + bool Init(); - template - std::string StorageRootPath (const Storage& storage, Filename... filenames) - { - std::stringstream s(""); - s << storage.GetRoot (); - _ExpandPath(s, filenames...); + /** + * @brief Get list of files in directory + * @param path Path to directory + * @param files Vector to store found files + * @return true on success and false if directory not exists + */ + bool ReadDir(const std::string &path, std::vector &files); - 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 + void _ExpandPath(std::stringstream &path, T c) { + path << i2p::fs::dirSep << c; + } + + template + void _ExpandPath(std::stringstream &path, T c, Other ... other) { + _ExpandPath(path, c); + _ExpandPath(path, other ...); + } + + /** + * @brief Get path relative to datadir + * + * Examples (with datadir = "/tmp/i2pd"): + * + * i2p::fs::Path("test") -> '/tmp/i2pd/test' + * i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt' + */ + template + std::string DataDirPath(Other ... components) { + std::stringstream s(""); + s << i2p::fs::GetDataDir(); + _ExpandPath(s, components ...); + + return s.str(); + } + + template + std::string StorageRootPath(const Storage &storage, Filename... filenames) { + std::stringstream s(""); + s << storage.GetRoot(); + _ExpandPath(s, filenames...); + + return s.str(); + } + + } // fs } // i2p #endif // /* FS_H__ */ diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index 9a0700d0..048d4c64 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -15,185 +15,160 @@ #include "Family.h" #include "Config.h" -namespace i2p -{ -namespace data -{ - Families::Families () - { - } +namespace i2p { + namespace data { + Families::Families() { + } - Families::~Families () - { - } + Families::~Families() { + } - void Families::LoadCertificate (const std::string& filename) - { - SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); - int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); - if (ret) - { - SSL * ssl = SSL_new (ctx); - X509 * cert = SSL_get_certificate (ssl); - if (cert) - { - std::shared_ptr verifier; - // extract issuer name - char name[100]; - X509_NAME_oneline (X509_get_issuer_name(cert), name, 100); - char * cn = strstr (name, "CN="); - if (cn) - { - cn += 3; - char * family = strstr (cn, ".family"); - if (family) family[0] = 0; - } - auto pkey = X509_get_pubkey (cert); - int keyType = EVP_PKEY_base_id (pkey); - switch (keyType) - { - case EVP_PKEY_DSA: - // TODO: - break; - case EVP_PKEY_EC: - { - EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); - if (ecKey) - { - auto group = EC_KEY_get0_group (ecKey); - if (group) - { - int curve = EC_GROUP_get_curve_name (group); - if (curve == NID_X9_62_prime256v1) - { - uint8_t signingKey[64]; - BIGNUM * x = BN_new(), * y = BN_new(); - EC_POINT_get_affine_coordinates_GFp (group, - EC_KEY_get0_public_key (ecKey), x, y, NULL); - i2p::crypto::bn2buf (x, signingKey, 32); - i2p::crypto::bn2buf (y, signingKey + 32, 32); - BN_free (x); BN_free (y); - verifier = std::make_shared(); - verifier->SetPublicKey (signingKey); - } - else - LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); - } - EC_KEY_free (ecKey); - } - break; - } - default: - LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported"); - } - 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::LoadCertificate(const std::string &filename) { + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + int ret = SSL_CTX_use_certificate_file(ctx, filename.c_str(), SSL_FILETYPE_PEM); + if (ret) { + SSL *ssl = SSL_new(ctx); + X509 *cert = SSL_get_certificate(ssl); + if (cert) { + std::shared_ptr verifier; + // extract issuer name + char name[100]; + X509_NAME_oneline(X509_get_issuer_name(cert), name, 100); + char *cn = strstr(name, "CN="); + if (cn) { + cn += 3; + char *family = strstr(cn, ".family"); + if (family) family[0] = 0; + } + auto pkey = X509_get_pubkey(cert); + int keyType = EVP_PKEY_base_id(pkey); + switch (keyType) { + case EVP_PKEY_DSA: + // TODO: + break; + case EVP_PKEY_EC: { + EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey); + if (ecKey) { + auto group = EC_KEY_get0_group(ecKey); + if (group) { + int curve = EC_GROUP_get_curve_name(group); + if (curve == NID_X9_62_prime256v1) { + uint8_t signingKey[64]; + BIGNUM *x = BN_new(), *y = BN_new(); + EC_POINT_get_affine_coordinates_GFp(group, + EC_KEY_get0_public_key(ecKey), x, y, NULL); + i2p::crypto::bn2buf(x, signingKey, 32); + i2p::crypto::bn2buf(y, signingKey + 32, 32); + BN_free(x); + BN_free(y); + verifier = std::make_shared(); + verifier->SetPublicKey(signingKey); + } else + LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported"); + } + EC_KEY_free(ecKey); + } + break; + } + default: + LogPrint(eLogWarning, "Family: Certificate key type ", keyType, " is not supported"); + } + 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 () - { - std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family"; + void Families::LoadCertificates() { + std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family"; - std::vector files; - int numCertificates = 0; + std::vector files; + int numCertificates = 0; - if (!i2p::fs::ReadDir(certDir, files)) { - LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir); - return; - } + if (!i2p::fs::ReadDir(certDir, files)) { + LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir); + return; + } - for (const std::string & file : files) { - if (file.compare(file.size() - 4, 4, ".crt") != 0) { - LogPrint(eLogWarning, "Family: ignoring file ", file); - continue; - } - LoadCertificate (file); - numCertificates++; - } - LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded"); - } + for (const std::string &file: files) { + if (file.compare(file.size() - 4, 4, ".crt") != 0) { + LogPrint(eLogWarning, "Family: ignoring file ", file); + continue; + } + LoadCertificate(file); + numCertificates++; + } + LogPrint(eLogInfo, "Family: ", numCertificates, " certificates loaded"); + } - bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, - const char * signature, const char * key) const - { - uint8_t buf[100], signatureBuf[64]; - size_t len = family.length (), signatureLen = strlen (signature); - if (len + 32 > 100) - { - LogPrint (eLogError, "Family: ", family, " is too long"); - return false; - } + bool Families::VerifyFamily(const std::string &family, const IdentHash &ident, + const char *signature, const char *key) const { + uint8_t buf[100], signatureBuf[64]; + size_t len = family.length(), signatureLen = strlen(signature); + if (len + 32 > 100) { + LogPrint(eLogError, "Family: ", family, " is too long"); + return false; + } - memcpy (buf, family.c_str (), len); - memcpy (buf + len, (const uint8_t *)ident, 32); - len += 32; - Base64ToByteStream (signature, signatureLen, signatureBuf, 64); - auto it = m_SigningKeys.find (family); - if (it != m_SigningKeys.end ()) - return it->second.first->Verify (buf, len, signatureBuf); - // TODO: process key - return true; - } + memcpy(buf, family.c_str(), len); + memcpy(buf + len, (const uint8_t *) ident, 32); + len += 32; + Base64ToByteStream(signature, signatureLen, signatureBuf, 64); + auto it = m_SigningKeys.find(family); + if (it != m_SigningKeys.end()) + return it->second.first->Verify(buf, len, signatureBuf); + // TODO: process key + return true; + } - FamilyID Families::GetFamilyID (const std::string& family) const - { - auto it = m_SigningKeys.find (family); - if (it != m_SigningKeys.end ()) - return it->second.second; - return 0; - } + FamilyID Families::GetFamilyID(const std::string &family) const { + auto it = m_SigningKeys.find(family); + if (it != m_SigningKeys.end()) + return it->second.second; + return 0; + } - std::string CreateFamilySignature (const std::string& family, const IdentHash& ident) - { - auto filename = i2p::fs::DataDirPath("family", (family + ".key")); - std::string sig; - SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); - int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); - if (ret) - { - SSL * ssl = SSL_new (ctx); - EVP_PKEY * pkey = SSL_get_privatekey (ssl); - EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); - if (ecKey) - { - auto group = EC_KEY_get0_group (ecKey); - if (group) - { - int curve = EC_GROUP_get_curve_name (group); - if (curve == NID_X9_62_prime256v1) - { - uint8_t signingPrivateKey[32], buf[50], signature[64]; - i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32); - i2p::crypto::ECDSAP256Signer signer (signingPrivateKey); - size_t len = family.length (); - memcpy (buf, family.c_str (), len); - memcpy (buf + len, (const uint8_t *)ident, 32); - len += 32; - signer.Sign (buf, len, signature); - len = Base64EncodingBufferSize (64); - char * b64 = new char[len+1]; - len = ByteStreamToBase64 (signature, 64, b64, len); - b64[len] = 0; - sig = b64; - delete[] b64; - } - else - LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); - } - } - SSL_free (ssl); - } - else - LogPrint (eLogError, "Family: Can't open keys file: ", filename); - SSL_CTX_free (ctx); - return sig; - } -} + std::string CreateFamilySignature(const std::string &family, const IdentHash &ident) { + auto filename = i2p::fs::DataDirPath("family", (family + ".key")); + std::string sig; + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + int ret = SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM); + if (ret) { + SSL *ssl = SSL_new(ctx); + EVP_PKEY *pkey = SSL_get_privatekey(ssl); + EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey); + if (ecKey) { + auto group = EC_KEY_get0_group(ecKey); + if (group) { + int curve = EC_GROUP_get_curve_name(group); + if (curve == NID_X9_62_prime256v1) { + uint8_t signingPrivateKey[32], buf[50], signature[64]; + i2p::crypto::bn2buf(EC_KEY_get0_private_key(ecKey), signingPrivateKey, 32); + i2p::crypto::ECDSAP256Signer signer(signingPrivateKey); + size_t len = family.length(); + memcpy(buf, family.c_str(), len); + memcpy(buf + len, (const uint8_t *) ident, 32); + len += 32; + signer.Sign(buf, len, signature); + len = Base64EncodingBufferSize(64); + char *b64 = new char[len + 1]; + len = ByteStreamToBase64(signature, 64, b64, len); + b64[len] = 0; + sig = b64; + delete[] b64; + } else + LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported"); + } + } + SSL_free(ssl); + } else + LogPrint(eLogError, "Family: Can't open keys file: ", filename); + SSL_CTX_free(ctx); + return sig; + } + } } diff --git a/libi2pd/Family.h b/libi2pd/Family.h index b19ea142..5077c909 100644 --- a/libi2pd/Family.h +++ b/libi2pd/Family.h @@ -15,34 +15,36 @@ #include "Signature.h" #include "Identity.h" -namespace i2p -{ -namespace data -{ - typedef int FamilyID; - class Families - { - public: +namespace i2p { + namespace data { + typedef int FamilyID; - Families (); - ~Families (); - 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; + class Families { + public: - private: + Families(); - void LoadCertificate (const std::string& filename); + ~Families(); - private: + void LoadCertificates(); - std::map, 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); - // return base64 signature of empty string in case of failure -} + FamilyID GetFamilyID(const std::string &family) const; + + private: + + void LoadCertificate(const std::string &filename); + + private: + + std::map, 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 diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 9daea1f0..5f49d184 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -22,1116 +22,993 @@ #include "ECIESX25519AEADRatchetSession.h" #include "Garlic.h" -namespace i2p -{ -namespace garlic -{ - GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet): - m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), - m_LeaseSetUpdateMsgID (0) - { - } +namespace i2p { + namespace garlic { + GarlicRoutingSession::GarlicRoutingSession(GarlicDestination *owner, bool attachLeaseSet) : + m_Owner(owner), m_LeaseSetUpdateStatus(attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), + m_LeaseSetUpdateMsgID(0) { + } - GarlicRoutingSession::GarlicRoutingSession (): - m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) - { - } + GarlicRoutingSession::GarlicRoutingSession() : + m_Owner(nullptr), m_LeaseSetUpdateStatus(eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID(0) { + } - GarlicRoutingSession::~GarlicRoutingSession () - { - } + GarlicRoutingSession::~GarlicRoutingSession() { + } - std::shared_ptr GarlicRoutingSession::GetSharedRoutingPath () - { - if (!m_SharedRoutingPath) return nullptr; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED || - !m_SharedRoutingPath->outboundTunnel->IsEstablished () || - ts*1000LL > m_SharedRoutingPath->remoteLease->endDate || - ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT) - m_SharedRoutingPath = nullptr; - if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++; - return m_SharedRoutingPath; - } + std::shared_ptr GarlicRoutingSession::GetSharedRoutingPath() { + if (!m_SharedRoutingPath) return nullptr; + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED || + !m_SharedRoutingPath->outboundTunnel->IsEstablished() || + ts * 1000LL > m_SharedRoutingPath->remoteLease->endDate || + ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT) + m_SharedRoutingPath = nullptr; + if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++; + return m_SharedRoutingPath; + } - void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr path) - { - if (path && path->outboundTunnel && path->remoteLease) - { - path->updateTime = i2p::util::GetSecondsSinceEpoch (); - path->numTimesUsed = 0; - } - else - path = nullptr; - m_SharedRoutingPath = path; - } + void GarlicRoutingSession::SetSharedRoutingPath(std::shared_ptr path) { + if (path && path->outboundTunnel && path->remoteLease) { + path->updateTime = i2p::util::GetSecondsSinceEpoch(); + path->numTimesUsed = 0; + } else + path = nullptr; + m_SharedRoutingPath = path; + } - bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID) - { - if (msgID == GetLeaseSetUpdateMsgID ()) - { - SetLeaseSetUpdateStatus (eLeaseSetUpToDate); - SetLeaseSetUpdateMsgID (0); - LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed"); - return true; - } - return false; - } + bool GarlicRoutingSession::MessageConfirmed(uint32_t msgID) { + if (msgID == GetLeaseSetUpdateMsgID()) { + SetLeaseSetUpdateStatus(eLeaseSetUpToDate); + SetLeaseSetUpdateMsgID(0); + LogPrint(eLogInfo, "Garlic: LeaseSet update confirmed"); + return true; + } + return false; + } - void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts) - { - if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) - { - if (GetOwner ()) - GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); - m_LeaseSetUpdateMsgID = 0; - } - } + void GarlicRoutingSession::CleanupUnconfirmedLeaseSet(uint64_t ts) { + if (m_LeaseSetUpdateMsgID && ts * 1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) { + if (GetOwner()) + GetOwner()->RemoveDeliveryStatusSession(m_LeaseSetUpdateMsgID); + m_LeaseSetUpdateMsgID = 0; + } + } - std::shared_ptr GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID) - { - auto msg = CreateDeliveryStatusMsg (msgID); - if (GetOwner ()) - { - //encrypt - uint8_t key[32], tag[32]; - RAND_bytes (key, 32); // random session key - RAND_bytes (tag, 32); // random session tag - GetOwner ()->SubmitSessionKey (key, tag); - ElGamalAESSession garlic (key, tag); - msg = garlic.WrapSingleMessage (msg); - } - return msg; - } + std::shared_ptr GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg(uint32_t msgID) { + auto msg = CreateDeliveryStatusMsg(msgID); + if (GetOwner()) { + //encrypt + uint8_t key[32], tag[32]; + RAND_bytes(key, 32); // random session key + RAND_bytes(tag, 32); // random session tag + GetOwner()->SubmitSessionKey(key, tag); + ElGamalAESSession garlic(key, tag); + msg = garlic.WrapSingleMessage(msg); + } + return msg; + } - ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, - std::shared_ptr destination, int numTags, bool attachLeaseSet): - GarlicRoutingSession (owner, attachLeaseSet), - m_Destination (destination), m_NumTags (numTags) - { - // create new session tags and session key - RAND_bytes (m_SessionKey, 32); - m_Encryption.SetKey (m_SessionKey); - } + ElGamalAESSession::ElGamalAESSession(GarlicDestination *owner, + std::shared_ptr destination, + int numTags, bool attachLeaseSet) : + GarlicRoutingSession(owner, attachLeaseSet), + m_Destination(destination), m_NumTags(numTags) { + // create new session tags and session key + RAND_bytes(m_SessionKey, 32); + m_Encryption.SetKey(m_SessionKey); + } - ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag): - m_NumTags(1) - { - memcpy (m_SessionKey, sessionKey, 32); - m_Encryption.SetKey (m_SessionKey); - m_SessionTags.push_back (sessionTag); - m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); - } + ElGamalAESSession::ElGamalAESSession(const uint8_t *sessionKey, const SessionTag &sessionTag) : + m_NumTags(1) { + memcpy(m_SessionKey, sessionKey, 32); + m_Encryption.SetKey(m_SessionKey); + m_SessionTags.push_back(sessionTag); + m_SessionTags.back().creationTime = i2p::util::GetSecondsSinceEpoch(); + } - std::shared_ptr ElGamalAESSession::WrapSingleMessage (std::shared_ptr msg) - { - auto m = NewI2NPMessage (); - m->Align (12); // in order to get buf aligned to 16 (12 + 4) - size_t len = 0; - uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + std::shared_ptr ElGamalAESSession::WrapSingleMessage(std::shared_ptr msg) { + auto m = NewI2NPMessage(); + m->Align(12); // in order to get buf aligned to 16 (12 + 4) + size_t len = 0; + uint8_t *buf = m->GetPayload() + 4; // 4 bytes for length - // find non-expired tag - bool tagFound = false; - SessionTag tag; - if (m_NumTags > 0) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - while (!m_SessionTags.empty ()) - { - if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - tag = m_SessionTags.front (); - m_SessionTags.pop_front (); // use same tag only once - tagFound = true; - break; - } - else - m_SessionTags.pop_front (); // remove expired tag - } - } - // create message - if (!tagFound) // new session - { - LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal"); - if (!m_Destination) - { - LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination"); - return nullptr; - } - // create ElGamal block - ElGamalBlock elGamal; - memcpy (elGamal.sessionKey, m_SessionKey, 32); - RAND_bytes (elGamal.preIV, 32); // Pre-IV - uint8_t iv[32]; // IV is first 16 bytes - SHA256(elGamal.preIV, 32, iv); - m_Destination->Encrypt ((uint8_t *)&elGamal, buf); - m_Encryption.SetIV (iv); - buf += 514; - len += 514; - } - else // existing session - { - // session tag - memcpy (buf, tag, 32); - uint8_t iv[32]; // IV is first 16 bytes - SHA256(tag, 32, iv); - m_Encryption.SetIV (iv); - buf += 32; - len += 32; - } - // AES block - len += CreateAESBlock (buf, msg); - htobe32buf (m->GetPayload (), len); - m->len += len + 4; - m->FillI2NPMessageHeader (eI2NPGarlic); - return m; - } + // find non-expired tag + bool tagFound = false; + SessionTag tag; + if (m_NumTags > 0) { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + while (!m_SessionTags.empty()) { + if (ts < m_SessionTags.front().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) { + tag = m_SessionTags.front(); + m_SessionTags.pop_front(); // use same tag only once + tagFound = true; + break; + } else + m_SessionTags.pop_front(); // remove expired tag + } + } + // create message + if (!tagFound) // new session + { + LogPrint(eLogInfo, "Garlic: No tags available, will use ElGamal"); + if (!m_Destination) { + LogPrint(eLogError, "Garlic: Can't use ElGamal for unknown destination"); + return nullptr; + } + // create ElGamal block + ElGamalBlock elGamal; + memcpy(elGamal.sessionKey, m_SessionKey, 32); + RAND_bytes(elGamal.preIV, 32); // Pre-IV + uint8_t iv[32]; // IV is first 16 bytes + SHA256(elGamal.preIV, 32, iv); + m_Destination->Encrypt((uint8_t * ) & elGamal, buf); + m_Encryption.SetIV(iv); + buf += 514; + len += 514; + } else // existing session + { + // session tag + memcpy(buf, tag, 32); + uint8_t iv[32]; // IV is first 16 bytes + SHA256(tag, 32, iv); + m_Encryption.SetIV(iv); + buf += 32; + len += 32; + } + // AES block + len += CreateAESBlock(buf, msg); + htobe32buf(m->GetPayload(), len); + m->len += len + 4; + m->FillI2NPMessageHeader(eI2NPGarlic); + return m; + } - size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) - { - size_t blockSize = 0; - bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); - UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; - htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count - blockSize += 2; - if (newTags) // session tags recreated - { - for (int i = 0; i < newTags->numTags; i++) - { - memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags - blockSize += 32; - } - } - uint32_t * payloadSize = (uint32_t *)(buf + blockSize); - blockSize += 4; - uint8_t * payloadHash = buf + blockSize; - blockSize += 32; - buf[blockSize] = 0; // flag - blockSize++; - size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); - htobe32buf (payloadSize, len); - SHA256(buf + blockSize, len, payloadHash); - blockSize += len; - size_t rem = blockSize % 16; - if (rem) - blockSize += (16-rem); //padding - m_Encryption.Encrypt(buf, blockSize, buf); - return blockSize; - } + size_t ElGamalAESSession::CreateAESBlock(uint8_t *buf, std::shared_ptr msg) { + size_t blockSize = 0; + bool createNewTags = GetOwner() && m_NumTags && ((int) m_SessionTags.size() <= m_NumTags * 2 / 3); + UnconfirmedTags *newTags = createNewTags ? GenerateSessionTags() : nullptr; + htobuf16(buf, newTags ? htobe16(newTags->numTags) : 0); // tag count + blockSize += 2; + if (newTags) // session tags recreated + { + for (int i = 0; i < newTags->numTags; i++) { + memcpy(buf + blockSize, newTags->sessionTags[i], 32); // tags + blockSize += 32; + } + } + uint32_t *payloadSize = (uint32_t * )(buf + blockSize); + blockSize += 4; + uint8_t *payloadHash = buf + blockSize; + blockSize += 32; + buf[blockSize] = 0; // flag + blockSize++; + size_t len = CreateGarlicPayload(buf + blockSize, msg, newTags); + htobe32buf(payloadSize, len); + SHA256(buf + blockSize, len, payloadHash); + blockSize += len; + size_t rem = blockSize % 16; + if (rem) + blockSize += (16 - rem); //padding + m_Encryption.Encrypt(buf, blockSize, buf); + return blockSize; + } - size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t msgID; - RAND_bytes ((uint8_t *)&msgID, 4); - size_t size = 0; - uint8_t * numCloves = payload + size; - *numCloves = 0; - size++; + size_t ElGamalAESSession::CreateGarlicPayload(uint8_t *payload, std::shared_ptr msg, + UnconfirmedTags *newTags) { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + uint32_t msgID; + RAND_bytes((uint8_t * ) & msgID, 4); + size_t size = 0; + uint8_t *numCloves = payload + size; + *numCloves = 0; + size++; - if (GetOwner ()) - { - // resubmit non-confirmed LeaseSet - if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) - { - SetLeaseSetUpdateStatus (eLeaseSetUpdated); - SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed - } + if (GetOwner()) { + // resubmit non-confirmed LeaseSet + if (GetLeaseSetUpdateStatus() == eLeaseSetSubmitted && + ts > GetLeaseSetSubmissionTime() + LEASET_CONFIRMATION_TIMEOUT) { + SetLeaseSetUpdateStatus(eLeaseSetUpdated); + SetSharedRoutingPath(nullptr); // invalidate path since leaseset was not confirmed + } - // attach DeviveryStatus if necessary - if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated - { - // clove is DeliveryStatus - auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); - if (cloveSize > 0) // successive? - { - size += cloveSize; - (*numCloves)++; - if (newTags) // new tags created - { - newTags->msgID = msgID; - m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr(newTags))); - newTags = nullptr; // got acquired - } - GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID); - } - else - LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created"); - } - // attach LeaseSet - if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) - { - if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous - SetLeaseSetUpdateStatus (eLeaseSetSubmitted); - SetLeaseSetUpdateMsgID (msgID); - SetLeaseSetSubmissionTime (ts); - // clove if our leaseSet must be attached - auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()); - size += CreateGarlicClove (payload + size, leaseSet, false); - (*numCloves)++; - } - } - if (msg) // clove message itself if presented - { - size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); - (*numCloves)++; - } - memset (payload + size, 0, 3); // certificate of message - size += 3; - htobe32buf (payload + size, msgID); // MessageID - size += 4; - htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec - size += 8; + // attach DeviveryStatus if necessary + if (newTags || GetLeaseSetUpdateStatus() == eLeaseSetUpdated) // new tags created or leaseset updated + { + // clove is DeliveryStatus + auto cloveSize = CreateDeliveryStatusClove(payload + size, msgID); + if (cloveSize > 0) // successive? + { + size += cloveSize; + (*numCloves)++; + if (newTags) // new tags created + { + newTags->msgID = msgID; + m_UnconfirmedTagsMsgs.insert( + std::make_pair(msgID, std::unique_ptr(newTags))); + newTags = nullptr; // got acquired + } + GetOwner()->DeliveryStatusSent(shared_from_this(), msgID); + } else + LogPrint(eLogWarning, "Garlic: DeliveryStatus clove was not created"); + } + // attach LeaseSet + if (GetLeaseSetUpdateStatus() == eLeaseSetUpdated) { + if (GetLeaseSetUpdateMsgID()) + GetOwner()->RemoveDeliveryStatusSession(GetLeaseSetUpdateMsgID()); // remove previous + SetLeaseSetUpdateStatus(eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID(msgID); + SetLeaseSetSubmissionTime(ts); + // clove if our leaseSet must be attached + auto leaseSet = CreateDatabaseStoreMsg(GetOwner()->GetLeaseSet()); + size += CreateGarlicClove(payload + size, leaseSet, false); + (*numCloves)++; + } + } + if (msg) // clove message itself if presented + { + size += CreateGarlicClove(payload + size, msg, m_Destination ? m_Destination->IsDestination() : false); + (*numCloves)++; + } + memset(payload + size, 0, 3); // certificate of message + size += 3; + htobe32buf(payload + size, msgID); // MessageID + size += 4; + htobe64buf(payload + size, ts + 8000); // Expiration of message, 8 sec + size += 8; - if (newTags) delete newTags; // not acquired, delete - return size; - } + if (newTags) delete newTags; // not acquired, delete + return size; + } - size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec - size_t size = 0; - if (isDestination) - { - buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination - size++; - memcpy (buf + size, m_Destination->GetIdentHash (), 32); - size += 32; - } - else - { - buf[size] = 0;// delivery instructions flag local - size++; - } + size_t + ElGamalAESSession::CreateGarlicClove(uint8_t *buf, std::shared_ptr msg, bool isDestination) { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch() + 8000; // 8 sec + size_t size = 0; + if (isDestination) { + buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination + size++; + memcpy(buf + size, m_Destination->GetIdentHash(), 32); + size += 32; + } else { + buf[size] = 0;// delivery instructions flag local + size++; + } - memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); - size += msg->GetLength (); - uint32_t cloveID; - RAND_bytes ((uint8_t *)&cloveID, 4); - htobe32buf (buf + size, cloveID); // CloveID - size += 4; - htobe64buf (buf + size, ts); // Expiration of clove - size += 8; - memset (buf + size, 0, 3); // certificate of clove - size += 3; - return size; - } + memcpy(buf + size, msg->GetBuffer(), msg->GetLength()); + size += msg->GetLength(); + uint32_t cloveID; + RAND_bytes((uint8_t * ) & cloveID, 4); + htobe32buf(buf + size, cloveID); // CloveID + size += 4; + htobe64buf(buf + size, ts); // Expiration of clove + size += 8; + memset(buf + size, 0, 3); // certificate of clove + size += 3; + return size; + } - size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) - { - size_t size = 0; - if (GetOwner ()) - { - auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); - if (inboundTunnel) - { - buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel - size++; - // hash and tunnelID sequence is reversed for Garlic - memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash - size += 32; - htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID - size += 4; - // create msg - auto msg = CreateEncryptedDeliveryStatusMsg (msgID); - if (msg) - { - memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); - size += msg->GetLength (); - } - // fill clove - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec - uint32_t cloveID; - RAND_bytes ((uint8_t *)&cloveID, 4); - htobe32buf (buf + size, cloveID); // CloveID - size += 4; - htobe64buf (buf + size, ts); // Expiration of clove - size += 8; - memset (buf + size, 0, 3); // certificate of clove - size += 3; - } - else - LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); - } - else - LogPrint (eLogWarning, "Garlic: Missing local LeaseSet"); + size_t ElGamalAESSession::CreateDeliveryStatusClove(uint8_t *buf, uint32_t msgID) { + size_t size = 0; + if (GetOwner()) { + auto inboundTunnel = GetOwner()->GetTunnelPool()->GetNextInboundTunnel(); + if (inboundTunnel) { + buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel + size++; + // hash and tunnelID sequence is reversed for Garlic + memcpy(buf + size, inboundTunnel->GetNextIdentHash(), 32); // To Hash + size += 32; + htobe32buf(buf + size, inboundTunnel->GetNextTunnelID()); // tunnelID + size += 4; + // create msg + auto msg = CreateEncryptedDeliveryStatusMsg(msgID); + if (msg) { + memcpy(buf + size, msg->GetBuffer(), msg->GetLength()); + size += msg->GetLength(); + } + // fill clove + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch() + 8000; // 8 sec + uint32_t cloveID; + RAND_bytes((uint8_t * ) & cloveID, 4); + htobe32buf(buf + size, cloveID); // CloveID + size += 4; + htobe64buf(buf + size, ts); // Expiration of clove + size += 8; + memset(buf + size, 0, 3); // certificate of clove + size += 3; + } else + LogPrint(eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); + } else + LogPrint(eLogWarning, "Garlic: Missing local LeaseSet"); - return size; - } + return size; + } - ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags () - { - auto tags = new UnconfirmedTags (m_NumTags); - tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); - for (int i = 0; i < m_NumTags; i++) - { - RAND_bytes (tags->sessionTags[i], 32); - tags->sessionTags[i].creationTime = tags->tagsCreationTime; - } - return tags; - } + ElGamalAESSession::UnconfirmedTags *ElGamalAESSession::GenerateSessionTags() { + auto tags = new UnconfirmedTags(m_NumTags); + tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch(); + for (int i = 0; i < m_NumTags; i++) { + RAND_bytes(tags->sessionTags[i], 32); + tags->sessionTags[i].creationTime = tags->tagsCreationTime; + } + return tags; + } - bool ElGamalAESSession::MessageConfirmed (uint32_t msgID) - { - TagsConfirmed (msgID); - if (!GarlicRoutingSession::MessageConfirmed (msgID)) - CleanupExpiredTags (); - return true; - } + bool ElGamalAESSession::MessageConfirmed(uint32_t msgID) { + TagsConfirmed(msgID); + if (!GarlicRoutingSession::MessageConfirmed(msgID)) + CleanupExpiredTags(); + return true; + } - void ElGamalAESSession::TagsConfirmed (uint32_t msgID) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - auto it = m_UnconfirmedTagsMsgs.find (msgID); - if (it != m_UnconfirmedTagsMsgs.end ()) - { - auto& tags = it->second; - if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - for (int i = 0; i < tags->numTags; i++) - m_SessionTags.push_back (tags->sessionTags[i]); - } - m_UnconfirmedTagsMsgs.erase (it); - } - } + void ElGamalAESSession::TagsConfirmed(uint32_t msgID) { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + auto it = m_UnconfirmedTagsMsgs.find(msgID); + if (it != m_UnconfirmedTagsMsgs.end()) { + auto &tags = it->second; + if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) { + for (int i = 0; i < tags->numTags; i++) + m_SessionTags.push_back(tags->sessionTags[i]); + } + m_UnconfirmedTagsMsgs.erase(it); + } + } - bool ElGamalAESSession::CleanupExpiredTags () - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) - { - if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - it = m_SessionTags.erase (it); - else - ++it; - } - CleanupUnconfirmedTags (); - CleanupUnconfirmedLeaseSet (ts); - return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); - } + bool ElGamalAESSession::CleanupExpiredTags() { + auto ts = i2p::util::GetSecondsSinceEpoch(); + for (auto it = m_SessionTags.begin(); it != m_SessionTags.end();) { + if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + it = m_SessionTags.erase(it); + else + ++it; + } + CleanupUnconfirmedTags(); + CleanupUnconfirmedLeaseSet(ts); + return !m_SessionTags.empty() || !m_UnconfirmedTagsMsgs.empty(); + } - bool ElGamalAESSession::CleanupUnconfirmedTags () - { - bool ret = false; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // delete expired unconfirmed tags - for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) - { - if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) - { - if (GetOwner ()) - GetOwner ()->RemoveDeliveryStatusSession (it->first); - it = m_UnconfirmedTagsMsgs.erase (it); - ret = true; - } - else - ++it; - } - return ret; - } + bool ElGamalAESSession::CleanupUnconfirmedTags() { + bool ret = false; + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + // delete expired unconfirmed tags + for (auto it = m_UnconfirmedTagsMsgs.begin(); it != m_UnconfirmedTagsMsgs.end();) { + if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) { + if (GetOwner()) + GetOwner()->RemoveDeliveryStatusSession(it->first); + it = m_UnconfirmedTagsMsgs.erase(it); + ret = true; + } else + ++it; + } + return ret; + } - GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default - m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard - { - } + GarlicDestination::GarlicDestination() : m_NumTags(32), // 32 tags by default + m_PayloadBuffer(nullptr), + m_NumRatchetInboundTags(0) // 0 means standard + { + } - GarlicDestination::~GarlicDestination () - { - if (m_PayloadBuffer) - delete[] m_PayloadBuffer; - } + GarlicDestination::~GarlicDestination() { + if (m_PayloadBuffer) + delete[] m_PayloadBuffer; + } - void GarlicDestination::CleanUp () - { - for (auto it: m_Sessions) - it.second->SetOwner (nullptr); - m_Sessions.clear (); - m_DeliveryStatusSessions.clear (); - m_Tags.clear (); - for (auto it: m_ECIESx25519Sessions) - { - it.second->Terminate (); - it.second->SetOwner (nullptr); - } - m_ECIESx25519Sessions.clear (); - m_ECIESx25519Tags.clear (); - } - void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) - { - if (key) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - m_Tags[SessionTag(tag, ts)] = std::make_shared(key); - } - } + void GarlicDestination::CleanUp() { + for (auto it: m_Sessions) + it.second->SetOwner(nullptr); + m_Sessions.clear(); + m_DeliveryStatusSessions.clear(); + m_Tags.clear(); + for (auto it: m_ECIESx25519Sessions) { + it.second->Terminate(); + it.second->SetOwner(nullptr); + } + m_ECIESx25519Sessions.clear(); + m_ECIESx25519Tags.clear(); + } - void GarlicDestination::AddECIESx25519Key (const uint8_t * key, const uint8_t * tag) - { - uint64_t t; - memcpy (&t, tag, 8); - AddECIESx25519Key (key, t); - } + void GarlicDestination::AddSessionKey(const uint8_t *key, const uint8_t *tag) { + if (key) { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + m_Tags[SessionTag(tag, ts)] = std::make_shared(key); + } + } - void GarlicDestination::AddECIESx25519Key (const uint8_t * key, uint64_t tag) - { - auto tagset = std::make_shared(this, key); - m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{0, tagset}); - } + void GarlicDestination::AddECIESx25519Key(const uint8_t *key, const uint8_t *tag) { + uint64_t t; + memcpy(&t, tag, 8); + AddECIESx25519Key(key, t); + } - bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) - { - AddSessionKey (key, tag); - return true; - } + void GarlicDestination::AddECIESx25519Key(const uint8_t *key, uint64_t tag) { + auto tagset = std::make_shared(this, key); + m_ECIESx25519Tags.emplace(tag, ECIESX25519AEADRatchetIndexTagset{0, tagset}); + } - void GarlicDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) - { - AddECIESx25519Key (key, tag); - } + bool GarlicDestination::SubmitSessionKey(const uint8_t *key, const uint8_t *tag) { + AddSessionKey(key, tag); + return true; + } - void GarlicDestination::HandleGarlicMessage (std::shared_ptr msg) - { - uint8_t * buf = msg->GetPayload (); - uint32_t length = bufbe32toh (buf); - if (length > msg->GetLength ()) - { - LogPrint (eLogWarning, "Garlic: Message length ", length, " exceeds I2NP message length ", msg->GetLength ()); - return; - } - auto mod = length & 0x0f; // %16 - buf += 4; // length + void GarlicDestination::SubmitECIESx25519Key(const uint8_t *key, uint64_t tag) { + AddECIESx25519Key(key, tag); + } - bool found = false; - if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - // try ECIESx25519 tag - found = HandleECIESx25519TagMessage (buf, length); - if (!found) - { - auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 - // AES tag might be used even if encryption type is not ElGamal/AES - if (it != m_Tags.end ()) // try AES tag - { - // tag found. Use AES - auto decryption = it->second; - m_Tags.erase (it); // tag might be used only once - if (length >= 32) - { - uint8_t iv[32]; // IV is first 16 bytes - SHA256(buf, 32, iv); - decryption->SetIV (iv); - decryption->Decrypt (buf + 32, length - 32, buf + 32); - HandleAESBlock (buf + 32, length - 32, decryption, msg->from); - found = true; - } - else - LogPrint (eLogWarning, "Garlic: Message length ", length, " is less than 32 bytes"); - } - if (!found) // assume new session - { - // AES tag not found. Handle depending on encryption type - // try ElGamal/AES first if leading block is 514 - ElGamalBlock elGamal; - if (mod == 2 && length >= 514 && SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) && - Decrypt (buf, (uint8_t *)&elGamal, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) - { - auto decryption = std::make_shared(elGamal.sessionKey); - uint8_t iv[32]; // IV is first 16 bytes - SHA256(elGamal.preIV, 32, iv); - decryption->SetIV (iv); - decryption->Decrypt(buf + 514, length - 514, buf + 514); - HandleAESBlock (buf + 514, length - 514, decryption, msg->from); - } - else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - { - // otherwise ECIESx25519 - auto session = std::make_shared (this, false); // incoming - if (!session->HandleNextMessage (buf, length, nullptr, 0)) - { - // try to generate more tags for last tagset - if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS)) - { - uint64_t missingTag; memcpy (&missingTag, buf, 8); - auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS); - LogPrint (eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags"); - for (int i = 0; i < maxTags; i++) - { - auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset); - if (!nextTag) - { - LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset"); - break; - } - if (nextTag == missingTag) - { - LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated"); - if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index)) - found = true; - break; - } - } - if (!found) m_LastTagset = nullptr; - } - if (!found) - LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); - } - } - else - LogPrint (eLogError, "Garlic: Failed to decrypt message"); - } - } - } + void GarlicDestination::HandleGarlicMessage(std::shared_ptr msg) { + uint8_t *buf = msg->GetPayload(); + uint32_t length = bufbe32toh(buf); + if (length > msg->GetLength()) { + LogPrint(eLogWarning, "Garlic: Message length ", length, " exceeds I2NP message length ", + msg->GetLength()); + return; + } + auto mod = length & 0x0f; // %16 + buf += 4; // length - bool GarlicDestination::HandleECIESx25519TagMessage (uint8_t * buf, size_t len) - { - uint64_t tag; - memcpy (&tag, buf, 8); - auto it = m_ECIESx25519Tags.find (tag); - if (it != m_ECIESx25519Tags.end ()) - { - if (it->second.tagset->HandleNextMessage (buf, len, it->second.index)) - m_LastTagset = it->second.tagset; - else - LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); - m_ECIESx25519Tags.erase (it); - return true; - } - return false; - } + bool found = false; + if (SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) + // try ECIESx25519 tag + found = HandleECIESx25519TagMessage(buf, length); + if (!found) { + auto it = !mod ? m_Tags.find(SessionTag(buf)) : m_Tags.end(); // AES block is multiple of 16 + // AES tag might be used even if encryption type is not ElGamal/AES + if (it != m_Tags.end()) // try AES tag + { + // tag found. Use AES + auto decryption = it->second; + m_Tags.erase(it); // tag might be used only once + if (length >= 32) { + uint8_t iv[32]; // IV is first 16 bytes + SHA256(buf, 32, iv); + decryption->SetIV(iv); + decryption->Decrypt(buf + 32, length - 32, buf + 32); + HandleAESBlock(buf + 32, length - 32, decryption, msg->from); + found = true; + } else + LogPrint(eLogWarning, "Garlic: Message length ", length, " is less than 32 bytes"); + } + if (!found) // assume new session + { + // AES tag not found. Handle depending on encryption type + // try ElGamal/AES first if leading block is 514 + ElGamalBlock elGamal; + if (mod == 2 && length >= 514 && SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) && + Decrypt(buf, (uint8_t * ) & elGamal, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) { + auto decryption = std::make_shared(elGamal.sessionKey); + uint8_t iv[32]; // IV is first 16 bytes + SHA256(elGamal.preIV, 32, iv); + decryption->SetIV(iv); + decryption->Decrypt(buf + 514, length - 514, buf + 514); + HandleAESBlock(buf + 514, length - 514, decryption, msg->from); + } else if (SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) { + // otherwise ECIESx25519 + auto session = std::make_shared(this, false); // incoming + if (!session->HandleNextMessage(buf, length, nullptr, 0)) { + // try to generate more tags for last tagset + if (m_LastTagset && (m_LastTagset->GetNextIndex() - m_LastTagset->GetTrimBehind() < + 3 * ECIESX25519_MAX_NUM_GENERATED_TAGS)) { + uint64_t missingTag; + memcpy(&missingTag, buf, 8); + auto maxTags = std::max(m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS); + LogPrint(eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags"); + for (int i = 0; i < maxTags; i++) { + auto nextTag = AddECIESx25519SessionNextTag(m_LastTagset); + if (!nextTag) { + LogPrint(eLogError, + "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset"); + break; + } + if (nextTag == missingTag) { + LogPrint(eLogDebug, + "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated"); + if (m_LastTagset->HandleNextMessage(buf, length, + m_ECIESx25519Tags[nextTag].index)) + found = true; + break; + } + } + if (!found) m_LastTagset = nullptr; + } + if (!found) + LogPrint(eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); + } + } else + LogPrint(eLogError, "Garlic: Failed to decrypt message"); + } + } + } - void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, - std::shared_ptr from) - { - uint16_t tagCount = bufbe16toh (buf); - buf += 2; len -= 2; - if (tagCount > 0) - { - if (tagCount*32 > len) - { - LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len); - return ; - } - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (int i = 0; i < tagCount; i++) - m_Tags[SessionTag(buf + i*32, ts)] = decryption; - } - buf += tagCount*32; - len -= tagCount*32; - uint32_t payloadSize = bufbe32toh (buf); - if (payloadSize > len) - { - LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize); - return; - } - buf += 4; - uint8_t * payloadHash = buf; - buf += 32;// payload hash. - if (*buf) // session key? - buf += 32; // new session key - buf++; // flag + bool GarlicDestination::HandleECIESx25519TagMessage(uint8_t *buf, size_t len) { + uint64_t tag; + memcpy(&tag, buf, 8); + auto it = m_ECIESx25519Tags.find(tag); + if (it != m_ECIESx25519Tags.end()) { + if (it->second.tagset->HandleNextMessage(buf, len, it->second.index)) + m_LastTagset = it->second.tagset; + else + LogPrint(eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); + m_ECIESx25519Tags.erase(it); + return true; + } + return false; + } - // payload - uint8_t digest[32]; - SHA256 (buf, payloadSize, digest); - if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match - { - LogPrint (eLogError, "Garlic: Wrong payload hash"); - return; - } - HandleGarlicPayload (buf, payloadSize, from); - } + void GarlicDestination::HandleAESBlock(uint8_t *buf, size_t len, std::shared_ptr decryption, + std::shared_ptr from) { + uint16_t tagCount = bufbe16toh(buf); + buf += 2; + len -= 2; + if (tagCount > 0) { + if (tagCount * 32 > len) { + LogPrint(eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len); + return; + } + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + for (int i = 0; i < tagCount; i++) + m_Tags[SessionTag(buf + i * 32, ts)] = decryption; + } + buf += tagCount * 32; + len -= tagCount * 32; + uint32_t payloadSize = bufbe32toh(buf); + if (payloadSize > len) { + LogPrint(eLogError, "Garlic: Unexpected payload size ", payloadSize); + return; + } + buf += 4; + uint8_t *payloadHash = buf; + buf += 32;// payload hash. + if (*buf) // session key? + buf += 32; // new session key + buf++; // flag - void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from) - { - if (len < 1) - { - LogPrint (eLogError, "Garlic: Payload is too short"); - return; - } - int numCloves = buf[0]; - LogPrint (eLogDebug, "Garlic: ", numCloves," cloves"); - buf++; len--; - for (int i = 0; i < numCloves; i++) - { - const uint8_t * buf1 = buf; - // delivery instructions - uint8_t flag = buf[0]; - buf++; // flag - if (flag & 0x80) // encrypted? - { - // TODO: implement - LogPrint (eLogWarning, "Garlic: Clove encrypted"); - buf += 32; - } - ptrdiff_t offset = buf - buf1; - GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); - switch (deliveryType) - { - case eGarlicDeliveryTypeLocal: - LogPrint (eLogDebug, "Garlic: Type local"); - if (offset > (int)len) - { - LogPrint (eLogError, "Garlic: Message is too short"); - break; - } - HandleI2NPMessage (buf, len - offset); - break; - case eGarlicDeliveryTypeDestination: - LogPrint (eLogDebug, "Garlic: Type destination"); - buf += 32; // destination. check it later or for multiple destinations - offset = buf - buf1; - if (offset > (int)len) - { - LogPrint (eLogError, "Garlic: Message is too short"); - break; - } - HandleI2NPMessage (buf, len - offset); - break; - case eGarlicDeliveryTypeTunnel: - { - LogPrint (eLogDebug, "Garlic: Type tunnel"); - // gwHash and gwTunnel sequence is reverted - uint8_t * gwHash = buf; - buf += 32; - offset = buf - buf1; - if (offset + 4 > (int)len) - { - LogPrint (eLogError, "Garlic: Message is too short"); - break; - } - uint32_t gwTunnel = bufbe32toh (buf); - buf += 4; offset += 4; - auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len - offset), from); - if (from) // received through an inbound tunnel - { - std::shared_ptr tunnel; - if (from->GetTunnelPool ()) - tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); - else - LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); - if (tunnel) // we have sent it through an outbound tunnel - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); - else - LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); - } - else // received directly - i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly - break; - } - case eGarlicDeliveryTypeRouter: - { - uint8_t * ident = buf; - buf += 32; - offset = buf - buf1; - if (!from) // received directly - { - if (offset > (int)len) - { - LogPrint (eLogError, "Garlic: Message is too short"); - break; - } - i2p::transport::transports.SendMessage (ident, - CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len - offset))); - } - else - LogPrint (eLogWarning, "Garlic: Type router for inbound tunnels not supported"); - break; - } - default: - LogPrint (eLogWarning, "Garlic: Unknown delivery type ", (int)deliveryType); - } - if (offset > (int)len) - { - LogPrint (eLogError, "Garlic: Message is too short"); - break; - } - buf += GetI2NPMessageLength (buf, len - offset); // I2NP - buf += 4; // CloveID - buf += 8; // Date - buf += 3; // Certificate - offset = buf - buf1; - if (offset > (int)len) - { - LogPrint (eLogError, "Garlic: Clove is too long"); - break; - } - len -= offset; - } - } + // payload + uint8_t digest[32]; + SHA256(buf, payloadSize, digest); + if (memcmp(payloadHash, digest, 32)) // payload hash doesn't match + { + LogPrint(eLogError, "Garlic: Wrong payload hash"); + return; + } + HandleGarlicPayload(buf, payloadSize, from); + } - std::shared_ptr GarlicDestination::WrapMessageForRouter (std::shared_ptr router, - std::shared_ptr msg) - { - if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ()); - else - { - auto session = GetRoutingSession (router, false); - return session->WrapSingleMessage (msg); - } - } + void GarlicDestination::HandleGarlicPayload(uint8_t *buf, size_t len, + std::shared_ptr from) { + if (len < 1) { + LogPrint(eLogError, "Garlic: Payload is too short"); + return; + } + int numCloves = buf[0]; + LogPrint(eLogDebug, "Garlic: ", numCloves, " cloves"); + buf++; + len--; + for (int i = 0; i < numCloves; i++) { + const uint8_t *buf1 = buf; + // delivery instructions + uint8_t flag = buf[0]; + buf++; // flag + if (flag & 0x80) // encrypted? + { + // TODO: implement + LogPrint(eLogWarning, "Garlic: Clove encrypted"); + buf += 32; + } + ptrdiff_t offset = buf - buf1; + GarlicDeliveryType deliveryType = (GarlicDeliveryType) ((flag >> 5) & 0x03); + switch (deliveryType) { + case eGarlicDeliveryTypeLocal: + LogPrint(eLogDebug, "Garlic: Type local"); + if (offset > (int) len) { + LogPrint(eLogError, "Garlic: Message is too short"); + break; + } + HandleI2NPMessage(buf, len - offset); + break; + case eGarlicDeliveryTypeDestination: + LogPrint(eLogDebug, "Garlic: Type destination"); + buf += 32; // destination. check it later or for multiple destinations + offset = buf - buf1; + if (offset > (int) len) { + LogPrint(eLogError, "Garlic: Message is too short"); + break; + } + HandleI2NPMessage(buf, len - offset); + break; + case eGarlicDeliveryTypeTunnel: { + LogPrint(eLogDebug, "Garlic: Type tunnel"); + // gwHash and gwTunnel sequence is reverted + uint8_t *gwHash = buf; + buf += 32; + offset = buf - buf1; + if (offset + 4 > (int) len) { + LogPrint(eLogError, "Garlic: Message is too short"); + break; + } + uint32_t gwTunnel = bufbe32toh(buf); + buf += 4; + offset += 4; + auto msg = CreateI2NPMessage(buf, GetI2NPMessageLength(buf, len - offset), from); + if (from) // received through an inbound tunnel + { + std::shared_ptr tunnel; + if (from->GetTunnelPool()) + tunnel = from->GetTunnelPool()->GetNextOutboundTunnel(); + else + LogPrint(eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); + if (tunnel) // we have sent it through an outbound tunnel + tunnel->SendTunnelDataMsg(gwHash, gwTunnel, msg); + else + LogPrint(eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); + } else // received directly + i2p::transport::transports.SendMessage(gwHash, i2p::CreateTunnelGatewayMsg(gwTunnel, + msg)); // send directly + break; + } + case eGarlicDeliveryTypeRouter: { + uint8_t *ident = buf; + buf += 32; + offset = buf - buf1; + if (!from) // received directly + { + if (offset > (int) len) { + LogPrint(eLogError, "Garlic: Message is too short"); + break; + } + i2p::transport::transports.SendMessage(ident, + CreateI2NPMessage(buf, GetI2NPMessageLength(buf, + len - + offset))); + } else + LogPrint(eLogWarning, "Garlic: Type router for inbound tunnels not supported"); + break; + } + default: + LogPrint(eLogWarning, "Garlic: Unknown delivery type ", (int) deliveryType); + } + if (offset > (int) len) { + LogPrint(eLogError, "Garlic: Message is too short"); + break; + } + buf += GetI2NPMessageLength(buf, len - offset); // I2NP + buf += 4; // CloveID + buf += 8; // Date + buf += 3; // Certificate + offset = buf - buf1; + if (offset > (int) len) { + LogPrint(eLogError, "Garlic: Clove is too long"); + break; + } + len -= offset; + } + } - std::shared_ptr GarlicDestination::GetRoutingSession ( - std::shared_ptr destination, bool attachLeaseSet) - { - if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && - SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - { - ECIESX25519AEADRatchetSessionPtr session; - uint8_t staticKey[32]; - destination->Encrypt (nullptr, staticKey); // we are supposed to get static key - auto it = m_ECIESx25519Sessions.find (staticKey); - if (it != m_ECIESx25519Sessions.end ()) - { - session = it->second; - if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) - { - LogPrint (eLogDebug, "Garlic: Session restarted"); - session = nullptr; - } - } - if (!session) - { - session = std::make_shared (this, true); - session->SetRemoteStaticKey (staticKey); - } - if (destination->IsDestination ()) - session->SetDestination (destination->GetIdentHash ()); // TODO: remove - return session; - } - else - { - ElGamalAESSessionPtr session; - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (destination->GetIdentHash ()); - if (it != m_Sessions.end ()) - session = it->second; - } - if (!session) - { - session = std::make_shared (this, destination, - attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests - std::unique_lock l(m_SessionsMutex); - m_Sessions[destination->GetIdentHash ()] = session; - } - return session; - } - return nullptr; - } + std::shared_ptr + GarlicDestination::WrapMessageForRouter(std::shared_ptr router, + std::shared_ptr msg) { + if (router->GetEncryptionType() == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + return WrapECIESX25519MessageForRouter(msg, router->GetIdentity()->GetEncryptionPublicKey()); + else { + auto session = GetRoutingSession(router, false); + return session->WrapSingleMessage(msg); + } + } - void GarlicDestination::CleanupExpiredTags () - { - // incoming - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numExpiredTags = 0; - for (auto it = m_Tags.begin (); it != m_Tags.end ();) - { - if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - numExpiredTags++; - it = m_Tags.erase (it); - } - else - ++it; - } - if (numExpiredTags > 0) - LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); + std::shared_ptr GarlicDestination::GetRoutingSession( + std::shared_ptr destination, bool attachLeaseSet) { + if (destination->GetEncryptionType() == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && + SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) { + ECIESX25519AEADRatchetSessionPtr session; + uint8_t staticKey[32]; + destination->Encrypt(nullptr, staticKey); // we are supposed to get static key + auto it = m_ECIESx25519Sessions.find(staticKey); + if (it != m_ECIESx25519Sessions.end()) { + session = it->second; + if (session->IsInactive(i2p::util::GetSecondsSinceEpoch())) { + LogPrint(eLogDebug, "Garlic: Session restarted"); + session = nullptr; + } + } + if (!session) { + session = std::make_shared(this, true); + session->SetRemoteStaticKey(staticKey); + } + if (destination->IsDestination()) + session->SetDestination(destination->GetIdentHash()); // TODO: remove + return session; + } else { + ElGamalAESSessionPtr session; + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find(destination->GetIdentHash()); + if (it != m_Sessions.end()) + session = it->second; + } + if (!session) { + session = std::make_shared(this, destination, + attachLeaseSet ? m_NumTags : 4, + attachLeaseSet); // specified num tags for connections and 4 for LS requests + std::unique_lock l(m_SessionsMutex); + m_Sessions[destination->GetIdentHash()] = session; + } + return session; + } + return nullptr; + } - // outgoing - { - std::unique_lock l(m_SessionsMutex); - for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) - { - it->second->GetSharedRoutingPath (); // delete shared path if necessary - if (!it->second->CleanupExpiredTags ()) - { - LogPrint (eLogInfo, "Garlic: Routing session to ", it->first.ToBase32 (), " deleted"); - it->second->SetOwner (nullptr); - it = m_Sessions.erase (it); - } - else - ++it; - } - } - // delivery status sessions - { - std::unique_lock l(m_DeliveryStatusSessionsMutex); - for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); ) - { - if (it->second->GetOwner () != this) - it = m_DeliveryStatusSessions.erase (it); - else - ++it; - } - } - // ECIESx25519 - for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();) - { - if (it->second->CheckExpired (ts)) - { - it->second->Terminate (); - it = m_ECIESx25519Sessions.erase (it); - } - else - ++it; - } + void GarlicDestination::CleanupExpiredTags() { + // incoming + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + int numExpiredTags = 0; + for (auto it = m_Tags.begin(); it != m_Tags.end();) { + if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { + numExpiredTags++; + it = m_Tags.erase(it); + } else + ++it; + } + if (numExpiredTags > 0) + LogPrint(eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64()); - numExpiredTags = 0; - for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) - { - if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index)) - { - it->second.tagset->DeleteSymmKey (it->second.index); - it = m_ECIESx25519Tags.erase (it); - numExpiredTags++; - } - else - { - auto session = it->second.tagset->GetSession (); - if (!session || session->IsTerminated()) - { - it = m_ECIESx25519Tags.erase (it); - numExpiredTags++; - } - else - ++it; - } - } - if (numExpiredTags > 0) - LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); - if (m_LastTagset && m_LastTagset->IsExpired (ts)) - m_LastTagset = nullptr; - } + // outgoing + { + std::unique_lock l(m_SessionsMutex); + for (auto it = m_Sessions.begin(); it != m_Sessions.end();) { + it->second->GetSharedRoutingPath(); // delete shared path if necessary + if (!it->second->CleanupExpiredTags()) { + LogPrint(eLogInfo, "Garlic: Routing session to ", it->first.ToBase32(), " deleted"); + it->second->SetOwner(nullptr); + it = m_Sessions.erase(it); + } else + ++it; + } + } + // delivery status sessions + { + std::unique_lock l(m_DeliveryStatusSessionsMutex); + for (auto it = m_DeliveryStatusSessions.begin(); it != m_DeliveryStatusSessions.end();) { + if (it->second->GetOwner() != this) + it = m_DeliveryStatusSessions.erase(it); + else + ++it; + } + } + // ECIESx25519 + for (auto it = m_ECIESx25519Sessions.begin(); it != m_ECIESx25519Sessions.end();) { + if (it->second->CheckExpired(ts)) { + it->second->Terminate(); + it = m_ECIESx25519Sessions.erase(it); + } else + ++it; + } - void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) - { - std::unique_lock l(m_DeliveryStatusSessionsMutex); - m_DeliveryStatusSessions.erase (msgID); - } + numExpiredTags = 0; + for (auto it = m_ECIESx25519Tags.begin(); it != m_ECIESx25519Tags.end();) { + if (it->second.tagset->IsExpired(ts) || it->second.tagset->IsIndexExpired(it->second.index)) { + it->second.tagset->DeleteSymmKey(it->second.index); + it = m_ECIESx25519Tags.erase(it); + numExpiredTags++; + } else { + auto session = it->second.tagset->GetSession(); + if (!session || session->IsTerminated()) { + it = m_ECIESx25519Tags.erase(it); + numExpiredTags++; + } else + ++it; + } + } + if (numExpiredTags > 0) + LogPrint(eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", + GetIdentHash().ToBase64()); + if (m_LastTagset && m_LastTagset->IsExpired(ts)) + m_LastTagset = nullptr; + } - void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID) - { - std::unique_lock l(m_DeliveryStatusSessionsMutex); - m_DeliveryStatusSessions[msgID] = session; - } + void GarlicDestination::RemoveDeliveryStatusSession(uint32_t msgID) { + std::unique_lock l(m_DeliveryStatusSessionsMutex); + m_DeliveryStatusSessions.erase(msgID); + } - void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID) - { - GarlicRoutingSessionPtr session; - { - std::unique_lock l(m_DeliveryStatusSessionsMutex); - auto it = m_DeliveryStatusSessions.find (msgID); - if (it != m_DeliveryStatusSessions.end ()) - { - session = it->second; - m_DeliveryStatusSessions.erase (it); - } - } - if (session) - { - session->MessageConfirmed (msgID); - LogPrint (eLogDebug, "Garlic: Message ", msgID, " acknowledged"); - } - } + void GarlicDestination::DeliveryStatusSent(GarlicRoutingSessionPtr session, uint32_t msgID) { + std::unique_lock l(m_DeliveryStatusSessionsMutex); + m_DeliveryStatusSessions[msgID] = session; + } - void GarlicDestination::SetLeaseSetUpdated () - { - { - std::unique_lock l(m_SessionsMutex); - for (auto& it: m_Sessions) - it.second->SetLeaseSetUpdated (); - } - for (auto& it: m_ECIESx25519Sessions) - it.second->SetLeaseSetUpdated (); - } + void GarlicDestination::HandleDeliveryStatusMessage(uint32_t msgID) { + GarlicRoutingSessionPtr session; + { + std::unique_lock l(m_DeliveryStatusSessionsMutex); + auto it = m_DeliveryStatusSessions.find(msgID); + if (it != m_DeliveryStatusSessions.end()) { + session = it->second; + m_DeliveryStatusSessions.erase(it); + } + } + if (session) { + session->MessageConfirmed(msgID); + LogPrint(eLogDebug, "Garlic: Message ", msgID, " acknowledged"); + } + } - void GarlicDestination::ProcessGarlicMessage (std::shared_ptr msg) - { - HandleGarlicMessage (msg); - } + void GarlicDestination::SetLeaseSetUpdated() { + { + std::unique_lock l(m_SessionsMutex); + for (auto &it: m_Sessions) + it.second->SetLeaseSetUpdated(); + } + for (auto &it: m_ECIESx25519Sessions) + it.second->SetLeaseSetUpdated(); + } - void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) - { - uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); - HandleDeliveryStatusMessage (msgID); - } + void GarlicDestination::ProcessGarlicMessage(std::shared_ptr msg) { + HandleGarlicMessage(msg); + } - void GarlicDestination::SaveTags () - { - if (m_Tags.empty ()) return; - std::string ident = GetIdentHash().ToBase32(); - std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); - std::ofstream f (path, std::ofstream::binary | std::ofstream::out | std::ofstream::trunc); - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // 4 bytes timestamp, 32 bytes tag, 32 bytes key - for (auto it: m_Tags) - { - if (ts < it.first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - f.write ((char *)&it.first.creationTime, 4); - f.write ((char *)it.first.data (), 32); - f.write ((char *)it.second->GetKey ().data (), 32); - } - } - } + void GarlicDestination::ProcessDeliveryStatusMessage(std::shared_ptr msg) { + uint32_t msgID = bufbe32toh(msg->GetPayload() + DELIVERY_STATUS_MSGID_OFFSET); + HandleDeliveryStatusMessage(msgID); + } - void GarlicDestination::LoadTags () - { - std::string ident = GetIdentHash().ToBase32(); - std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < i2p::fs::GetLastUpdateTime (path) + INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - // might contain non-expired tags - std::ifstream f (path, std::ifstream::binary); - if (f) - { - std::map > keys; - // 4 bytes timestamp, 32 bytes tag, 32 bytes key - while (!f.eof ()) - { - uint32_t t; - uint8_t tag[32], key[32]; - f.read ((char *)&t, 4); if (f.eof ()) break; - if (ts < t + INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - f.read ((char *)tag, 32); - f.read ((char *)key, 32); - } - else - f.seekg (64, std::ios::cur); // skip - if (f.eof ()) break; + void GarlicDestination::SaveTags() { + if (m_Tags.empty()) return; + std::string ident = GetIdentHash().ToBase32(); + std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); + std::ofstream f(path, std::ofstream::binary | std::ofstream::out | std::ofstream::trunc); + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + // 4 bytes timestamp, 32 bytes tag, 32 bytes key + for (auto it: m_Tags) { + if (ts < it.first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { + f.write((char *) &it.first.creationTime, 4); + f.write((char *) it.first.data(), 32); + f.write((char *) it.second->GetKey().data(), 32); + } + } + } - std::shared_ptr decryption; - auto it = keys.find (key); - if (it != keys.end ()) - decryption = it->second; - else - decryption = std::make_shared(key); - m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption)); - } - if (!m_Tags.empty ()) - LogPrint (eLogInfo, "Garlic: ", m_Tags.size (), " tags loaded for ", ident); - } - } - i2p::fs::Remove (path); - } + void GarlicDestination::LoadTags() { + std::string ident = GetIdentHash().ToBase32(); + std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + if (ts < i2p::fs::GetLastUpdateTime(path) + INCOMING_TAGS_EXPIRATION_TIMEOUT) { + // might contain non-expired tags + std::ifstream f(path, std::ifstream::binary); + if (f) { + std::map > keys; + // 4 bytes timestamp, 32 bytes tag, 32 bytes key + while (!f.eof()) { + uint32_t t; + uint8_t tag[32], key[32]; + f.read((char *) &t, 4); + if (f.eof()) break; + if (ts < t + INCOMING_TAGS_EXPIRATION_TIMEOUT) { + f.read((char *) tag, 32); + f.read((char *) key, 32); + } else + f.seekg(64, std::ios::cur); // skip + if (f.eof()) break; - void CleanUpTagsFiles () - { - std::vector files; - i2p::fs::ReadDir (i2p::fs::DataDirPath("tags"), files); - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it: files) - if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) - i2p::fs::Remove (it); - } + std::shared_ptr decryption; + auto it = keys.find(key); + if (it != keys.end()) + decryption = it->second; + else + decryption = std::make_shared(key); + m_Tags.insert(std::make_pair(SessionTag(tag, ts), decryption)); + } + if (!m_Tags.empty()) + LogPrint(eLogInfo, "Garlic: ", m_Tags.size(), " tags loaded for ", ident); + } + } + i2p::fs::Remove(path); + } - void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len) - { - const uint8_t * buf1 = buf; - uint8_t flag = buf[0]; buf++; // flag - GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); - switch (deliveryType) - { - case eGarlicDeliveryTypeDestination: - LogPrint (eLogDebug, "Garlic: Type destination"); - buf += 32; // TODO: check destination + void CleanUpTagsFiles() { + std::vector files; + i2p::fs::ReadDir(i2p::fs::DataDirPath("tags"), files); + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + for (auto it: files) + if (ts >= i2p::fs::GetLastUpdateTime(it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) + i2p::fs::Remove(it); + } + + void GarlicDestination::HandleECIESx25519GarlicClove(const uint8_t *buf, size_t len) { + const uint8_t *buf1 = buf; + uint8_t flag = buf[0]; + buf++; // flag + GarlicDeliveryType deliveryType = (GarlicDeliveryType) ((flag >> 5) & 0x03); + switch (deliveryType) { + case eGarlicDeliveryTypeDestination: + LogPrint(eLogDebug, "Garlic: Type destination"); + buf += 32; // TODO: check destination #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; + [[fallthrough]]; #endif - // no break here - case eGarlicDeliveryTypeLocal: - { - LogPrint (eLogDebug, "Garlic: Type local"); - I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid - int32_t msgID = bufbe32toh (buf); buf += 4; // msgID - buf += 4; // expiration - ptrdiff_t offset = buf - buf1; - if (offset <= (int)len) - HandleCloveI2NPMessage (typeID, buf, len - offset, msgID); - else - LogPrint (eLogError, "Garlic: Clove is too long"); - break; - } - case eGarlicDeliveryTypeTunnel: - { - LogPrint (eLogDebug, "Garlic: Type tunnel"); - // gwHash and gwTunnel sequence is reverted - const uint8_t * gwHash = buf; - buf += 32; - ptrdiff_t offset = buf - buf1; - if (offset + 13 > (int)len) - { - LogPrint (eLogError, "Garlic: Message is too short"); - break; - } - uint32_t gwTunnel = bufbe32toh (buf); buf += 4; - I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid - uint32_t msgID = bufbe32toh (buf); buf += 4; // msgID - buf += 4; // expiration - offset += 13; - if (GetTunnelPool ()) - { - auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); - if (tunnel) - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset, msgID)); - else - LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); - } - else - LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); - break; - } - default: - LogPrint (eLogWarning, "Garlic: Unexpected delivery type ", (int)deliveryType); - } - } + // no break here + case eGarlicDeliveryTypeLocal: { + LogPrint(eLogDebug, "Garlic: Type local"); + I2NPMessageType typeID = (I2NPMessageType) (buf[0]); + buf++; // typeid + int32_t msgID = bufbe32toh(buf); + buf += 4; // msgID + buf += 4; // expiration + ptrdiff_t offset = buf - buf1; + if (offset <= (int) len) + HandleCloveI2NPMessage(typeID, buf, len - offset, msgID); + else + LogPrint(eLogError, "Garlic: Clove is too long"); + break; + } + case eGarlicDeliveryTypeTunnel: { + LogPrint(eLogDebug, "Garlic: Type tunnel"); + // gwHash and gwTunnel sequence is reverted + const uint8_t *gwHash = buf; + buf += 32; + ptrdiff_t offset = buf - buf1; + if (offset + 13 > (int) len) { + LogPrint(eLogError, "Garlic: Message is too short"); + break; + } + uint32_t gwTunnel = bufbe32toh(buf); + buf += 4; + I2NPMessageType typeID = (I2NPMessageType) (buf[0]); + buf++; // typeid + uint32_t msgID = bufbe32toh(buf); + buf += 4; // msgID + buf += 4; // expiration + offset += 13; + if (GetTunnelPool()) { + auto tunnel = GetTunnelPool()->GetNextOutboundTunnel(); + if (tunnel) + tunnel->SendTunnelDataMsg(gwHash, gwTunnel, + CreateI2NPMessage(typeID, buf, len - offset, msgID)); + else + LogPrint(eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); + } else + LogPrint(eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); + break; + } + default: + LogPrint(eLogWarning, "Garlic: Unexpected delivery type ", (int) deliveryType); + } + } - uint64_t GarlicDestination::AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset) - { - auto index = tagset->GetNextIndex (); - uint64_t tag = tagset->GetNextSessionTag (); - if (tag) - m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset}); - return tag; - } + uint64_t GarlicDestination::AddECIESx25519SessionNextTag(ReceiveRatchetTagSetPtr tagset) { + auto index = tagset->GetNextIndex(); + uint64_t tag = tagset->GetNextSessionTag(); + if (tag) + m_ECIESx25519Tags.emplace(tag, ECIESX25519AEADRatchetIndexTagset{index, tagset}); + return tag; + } - void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) - { - i2p::data::Tag<32> staticKeyTag (staticKey); - auto it = m_ECIESx25519Sessions.find (staticKeyTag); - if (it != m_ECIESx25519Sessions.end ()) - { - if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ())) - { - it->second->Terminate (); // detach - m_ECIESx25519Sessions.erase (it); - } - else - { - LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); - return; - } - } - m_ECIESx25519Sessions.emplace (staticKeyTag, session); - } + void + GarlicDestination::AddECIESx25519Session(const uint8_t *staticKey, ECIESX25519AEADRatchetSessionPtr session) { + i2p::data::Tag<32> staticKeyTag(staticKey); + auto it = m_ECIESx25519Sessions.find(staticKeyTag); + if (it != m_ECIESx25519Sessions.end()) { + if (it->second->CanBeRestarted(i2p::util::GetSecondsSinceEpoch())) { + it->second->Terminate(); // detach + m_ECIESx25519Sessions.erase(it); + } else { + LogPrint(eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64(), + " already exists"); + return; + } + } + m_ECIESx25519Sessions.emplace(staticKeyTag, session); + } - void GarlicDestination::RemoveECIESx25519Session (const uint8_t * staticKey) - { - auto it = m_ECIESx25519Sessions.find (staticKey); - if (it != m_ECIESx25519Sessions.end ()) - { - it->second->Terminate (); - m_ECIESx25519Sessions.erase (it); - } - } + void GarlicDestination::RemoveECIESx25519Session(const uint8_t *staticKey) { + auto it = m_ECIESx25519Sessions.find(staticKey); + if (it != m_ECIESx25519Sessions.end()) { + it->second->Terminate(); + m_ECIESx25519Sessions.erase(it); + } + } - uint8_t * GarlicDestination::GetPayloadBuffer () - { - if (!m_PayloadBuffer) - m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; - return m_PayloadBuffer; - } -} + uint8_t *GarlicDestination::GetPayloadBuffer() { + if (!m_PayloadBuffer) + m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; + return m_PayloadBuffer; + } + } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index b926abda..33ad32de 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -22,291 +22,346 @@ #include "Queue.h" #include "Identity.h" -namespace i2p -{ -namespace tunnel -{ - class OutboundTunnel; -} +namespace i2p { + namespace tunnel { + class OutboundTunnel; + } -namespace garlic -{ + namespace garlic { - enum GarlicDeliveryType - { - eGarlicDeliveryTypeLocal = 0, - eGarlicDeliveryTypeDestination = 1, - eGarlicDeliveryTypeRouter = 2, - eGarlicDeliveryTypeTunnel = 3 - }; + enum GarlicDeliveryType { + eGarlicDeliveryTypeLocal = 0, + eGarlicDeliveryTypeDestination = 1, + eGarlicDeliveryTypeRouter = 2, + eGarlicDeliveryTypeTunnel = 3 + }; - struct ElGamalBlock - { - uint8_t sessionKey[32]; - uint8_t preIV[32]; - uint8_t padding[158]; - }; + struct ElGamalBlock { + uint8_t sessionKey[32]; + uint8_t preIV[32]; + uint8_t padding[158]; + }; - const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes - const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes - const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds - const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds - 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 INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes + const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes + const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds + const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds + const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds + 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 - SessionTag (SessionTag&& ) = default; - SessionTag& operator= (SessionTag&& ) = default; + + SessionTag(SessionTag &&) = default; + + SessionTag &operator=(SessionTag &&) = default; + #endif - uint32_t creationTime; // seconds since epoch - }; + uint32_t creationTime; // seconds since epoch + }; - // AESDecryption is associated with session tags and store key - class AESDecryption: public i2p::crypto::CBCDecryption - { - public: + // AESDecryption is associated with session tags and store key + class AESDecryption : public i2p::crypto::CBCDecryption { + public: - AESDecryption (const uint8_t * key): m_Key (key) - { - SetKey (key); - } - const i2p::crypto::AESKey& GetKey () const { return m_Key; }; + AESDecryption(const uint8_t *key) : m_Key(key) { + SetKey(key); + } - private: + const i2p::crypto::AESKey &GetKey() const { return m_Key; }; - i2p::crypto::AESKey m_Key; - }; + private: - struct GarlicRoutingPath - { - std::shared_ptr outboundTunnel; - std::shared_ptr remoteLease; - int rtt; // RTT - uint32_t updateTime; // seconds since epoch - int numTimesUsed; - }; + i2p::crypto::AESKey m_Key; + }; - class GarlicDestination; - class GarlicRoutingSession - { - protected: + struct GarlicRoutingPath { + std::shared_ptr outboundTunnel; + std::shared_ptr remoteLease; + int rtt; // RTT + uint32_t updateTime; // seconds since epoch + int numTimesUsed; + }; - enum LeaseSetUpdateStatus - { - eLeaseSetUpToDate = 0, - eLeaseSetUpdated, - eLeaseSetSubmitted, - eLeaseSetDoNotSend - }; + class GarlicDestination; - public: + class GarlicRoutingSession { + protected: - GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet); - GarlicRoutingSession (); - virtual ~GarlicRoutingSession (); - virtual std::shared_ptr WrapSingleMessage (std::shared_ptr msg) = 0; - virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession - virtual bool 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 + enum LeaseSetUpdateStatus { + eLeaseSetUpToDate = 0, + eLeaseSetUpdated, + eLeaseSetSubmitted, + eLeaseSetDoNotSend + }; - void SetLeaseSetUpdated () - { - 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); + public: - std::shared_ptr GetSharedRoutingPath (); - void SetSharedRoutingPath (std::shared_ptr path); + GarlicRoutingSession(GarlicDestination *owner, bool attachLeaseSet); - GarlicDestination * GetOwner () const { return m_Owner; } - void SetOwner (GarlicDestination * owner) { m_Owner = owner; } + GarlicRoutingSession(); - protected: + virtual ~GarlicRoutingSession(); - LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } - void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } - uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; } - void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; } - void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; } + virtual std::shared_ptr WrapSingleMessage(std::shared_ptr msg) = 0; - std::shared_ptr 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; - uint32_t m_LeaseSetUpdateMsgID; - uint64_t m_LeaseSetSubmissionTime; // in milliseconds + virtual bool IsTerminated() const { return !GetOwner(); }; - std::shared_ptr 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 - virtual size_t GetNumOutgoingTags () const { return 0; }; - }; - //using GarlicRoutingSessionPtr = std::shared_ptr; - typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 + bool IsLeaseSetNonConfirmed() const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; }; - class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this - { - struct UnconfirmedTags - { - UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; - ~UnconfirmedTags () { delete[] sessionTags; }; - uint32_t msgID; - int numTags; - SessionTag * sessionTags; - uint32_t tagsCreationTime; - }; + bool IsLeaseSetUpdated() const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; }; - public: + uint64_t GetLeaseSetSubmissionTime() const { return m_LeaseSetSubmissionTime; } - ElGamalAESSession (GarlicDestination * owner, std::shared_ptr destination, - int numTags, bool attachLeaseSet); - ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption - ~ElGamalAESSession () {}; + void CleanupUnconfirmedLeaseSet(uint64_t ts); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + std::shared_ptr GetSharedRoutingPath(); - bool MessageConfirmed (uint32_t msgID); - bool CleanupExpiredTags (); // returns true if something left - bool CleanupUnconfirmedTags (); // returns true if something has been deleted + void SetSharedRoutingPath(std::shared_ptr path); - private: + GarlicDestination *GetOwner() const { return m_Owner; } - size_t CreateAESBlock (uint8_t * buf, std::shared_ptr msg); - size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags); - size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination); - size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); + void SetOwner(GarlicDestination *owner) { m_Owner = owner; } - void TagsConfirmed (uint32_t msgID); - UnconfirmedTags * GenerateSessionTags (); + protected: - private: + LeaseSetUpdateStatus GetLeaseSetUpdateStatus() const { return m_LeaseSetUpdateStatus; } - std::shared_ptr m_Destination; + void SetLeaseSetUpdateStatus(LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } - i2p::crypto::AESKey m_SessionKey; - std::list m_SessionTags; - int m_NumTags; - std::map > m_UnconfirmedTagsMsgs; // msgID->tags + uint32_t GetLeaseSetUpdateMsgID() const { return m_LeaseSetUpdateMsgID; } - 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 - size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; - }; - typedef std::shared_ptr ElGamalAESSessionPtr; + std::shared_ptr CreateEncryptedDeliveryStatusMsg(uint32_t msgID); - class ECIESX25519AEADRatchetSession; - typedef std::shared_ptr ECIESX25519AEADRatchetSessionPtr; - class ReceiveRatchetTagSet; - typedef std::shared_ptr ReceiveRatchetTagSetPtr; - struct ECIESX25519AEADRatchetIndexTagset - { - int index; - ReceiveRatchetTagSetPtr tagset; - }; + private: - class GarlicDestination: public i2p::data::LocalDestination - { - public: + GarlicDestination *m_Owner; - GarlicDestination (); - ~GarlicDestination (); + LeaseSetUpdateStatus m_LeaseSetUpdateStatus; + uint32_t m_LeaseSetUpdateMsgID; + uint64_t m_LeaseSetSubmissionTime; // in milliseconds - 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 GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); - void CleanupExpiredTags (); - void RemoveDeliveryStatusSession (uint32_t msgID); - std::shared_ptr WrapMessageForRouter (std::shared_ptr router, - std::shared_ptr msg); + std::shared_ptr m_SharedRoutingPath; - 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 (); + public: - virtual void ProcessGarlicMessage (std::shared_ptr msg); - virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); - virtual void SetLeaseSetUpdated (); + // for HTTP only + virtual size_t GetNumOutgoingTags() const { return 0; }; + }; - virtual std::shared_ptr GetLeaseSet () = 0; // TODO - virtual std::shared_ptr GetTunnelPool () const = 0; + //using GarlicRoutingSessionPtr = std::shared_ptr; + typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 - protected: + class ElGamalAESSession : public GarlicRoutingSession, public std::enable_shared_from_this { + 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 - 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 msg); - void HandleDeliveryStatusMessage (uint32_t msgID); + ~UnconfirmedTags() { delete[] sessionTags; }; + uint32_t msgID; + int numTags; + SessionTag *sessionTags; + uint32_t tagsCreationTime; + }; - void SaveTags (); - void LoadTags (); + public: - private: + ElGamalAESSession(GarlicDestination *owner, + std::shared_ptr destination, + int numTags, bool attachLeaseSet); - void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, - std::shared_ptr from); - void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from); + ElGamalAESSession(const uint8_t *sessionKey, const SessionTag &sessionTag); // one time encryption + ~ElGamalAESSession() {}; - private: + std::shared_ptr WrapSingleMessage(std::shared_ptr msg); - // outgoing sessions - int m_NumTags; - std::mutex m_SessionsMutex; - std::unordered_map m_Sessions; - std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session - uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet - // incoming - int m_NumRatchetInboundTags; - std::unordered_map, std::hash > > m_Tags; - std::unordered_map m_ECIESx25519Tags; // session tag -> session - ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for - // DeliveryStatus - std::mutex m_DeliveryStatusSessionsMutex; - std::unordered_map m_DeliveryStatusSessions; // msgID -> session + bool MessageConfirmed(uint32_t msgID); - public: + bool CleanupExpiredTags(); // returns true if something left + bool CleanupUnconfirmedTags(); // returns true if something has been deleted - // 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; } - }; + private: - void CleanUpTagsFiles (); + size_t CreateAESBlock(uint8_t *buf, std::shared_ptr msg); -} + size_t + CreateGarlicPayload(uint8_t *payload, std::shared_ptr msg, UnconfirmedTags *newTags); + + size_t CreateGarlicClove(uint8_t *buf, std::shared_ptr msg, bool isDestination); + + size_t CreateDeliveryStatusClove(uint8_t *buf, uint32_t msgID); + + void TagsConfirmed(uint32_t msgID); + + UnconfirmedTags *GenerateSessionTags(); + + private: + + std::shared_ptr m_Destination; + + i2p::crypto::AESKey m_SessionKey; + std::list m_SessionTags; + int m_NumTags; + std::map > m_UnconfirmedTagsMsgs; // msgID->tags + + i2p::crypto::CBCEncryption m_Encryption; + + public: + + // for HTTP only + size_t GetNumOutgoingTags() const { return m_SessionTags.size(); }; + }; + + typedef std::shared_ptr ElGamalAESSessionPtr; + + class ECIESX25519AEADRatchetSession; + + typedef std::shared_ptr ECIESX25519AEADRatchetSessionPtr; + + class ReceiveRatchetTagSet; + + typedef std::shared_ptr 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 + GetRoutingSession(std::shared_ptr destination, bool attachLeaseSet); + + void CleanupExpiredTags(); + + void RemoveDeliveryStatusSession(uint32_t msgID); + + std::shared_ptr WrapMessageForRouter(std::shared_ptr router, + std::shared_ptr 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 msg); + + virtual void ProcessDeliveryStatusMessage(std::shared_ptr msg); + + virtual void SetLeaseSetUpdated(); + + virtual std::shared_ptr GetLeaseSet() = 0; // TODO + virtual std::shared_ptr 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 msg); + + void HandleDeliveryStatusMessage(uint32_t msgID); + + void SaveTags(); + + void LoadTags(); + + private: + + void HandleAESBlock(uint8_t *buf, size_t len, std::shared_ptr decryption, + std::shared_ptr from); + + void HandleGarlicPayload(uint8_t *buf, size_t len, std::shared_ptr from); + + private: + + // outgoing sessions + int m_NumTags; + std::mutex m_SessionsMutex; + std::unordered_map m_Sessions; + std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session + uint8_t *m_PayloadBuffer; // for ECIESX25519AEADRatchet + // incoming + int m_NumRatchetInboundTags; + std::unordered_map, std::hash > > m_Tags; + std::unordered_map m_ECIESx25519Tags; // session tag -> session + ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for + // DeliveryStatus + std::mutex m_DeliveryStatusSessionsMutex; + std::unordered_map 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 diff --git a/libi2pd/Gost.cpp b/libi2pd/Gost.cpp index 2dafc9ae..fd0bf97a 100644 --- a/libi2pd/Gost.cpp +++ b/libi2pd/Gost.cpp @@ -14,998 +14,970 @@ #include "I2PEndian.h" #include "Gost.h" -namespace i2p -{ -namespace crypto -{ +namespace i2p { + namespace crypto { // GOST R 34.10 - GOSTR3410Curve::GOSTR3410Curve (BIGNUM * a, BIGNUM * b, BIGNUM * p, BIGNUM * q, BIGNUM * x, BIGNUM * y) - { - m_KeyLen = BN_num_bytes (p); - BN_CTX * ctx = BN_CTX_new (); - m_Group = EC_GROUP_new_curve_GFp (p, a, b, ctx); - EC_POINT * P = EC_POINT_new (m_Group); - EC_POINT_set_affine_coordinates_GFp (m_Group, P, x, y, ctx); - EC_GROUP_set_generator (m_Group, P, q, nullptr); - EC_GROUP_set_curve_name (m_Group, NID_id_GostR3410_2001); - EC_POINT_free(P); - BN_CTX_free (ctx); - } + GOSTR3410Curve::GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y) { + m_KeyLen = BN_num_bytes(p); + BN_CTX *ctx = BN_CTX_new(); + m_Group = EC_GROUP_new_curve_GFp(p, a, b, ctx); + EC_POINT *P = EC_POINT_new(m_Group); + EC_POINT_set_affine_coordinates_GFp(m_Group, P, x, y, ctx); + EC_GROUP_set_generator(m_Group, P, q, nullptr); + EC_GROUP_set_curve_name(m_Group, NID_id_GostR3410_2001); + EC_POINT_free(P); + BN_CTX_free(ctx); + } - GOSTR3410Curve::~GOSTR3410Curve () - { - EC_GROUP_free (m_Group); - } + GOSTR3410Curve::~GOSTR3410Curve() { + EC_GROUP_free(m_Group); + } - EC_POINT * GOSTR3410Curve::MulP (const BIGNUM * n) const - { - BN_CTX * ctx = BN_CTX_new (); - auto p = EC_POINT_new (m_Group); - EC_POINT_mul (m_Group, p, n, nullptr, nullptr, ctx); - BN_CTX_free (ctx); - return p; - } + EC_POINT *GOSTR3410Curve::MulP(const BIGNUM *n) const { + BN_CTX *ctx = BN_CTX_new(); + auto p = EC_POINT_new(m_Group); + EC_POINT_mul(m_Group, p, n, nullptr, nullptr, ctx); + BN_CTX_free(ctx); + return p; + } - bool GOSTR3410Curve::GetXY (const EC_POINT * p, BIGNUM * x, BIGNUM * y) const - { - return EC_POINT_get_affine_coordinates_GFp (m_Group, p, x, y, nullptr); - } + bool GOSTR3410Curve::GetXY(const EC_POINT *p, BIGNUM *x, BIGNUM *y) const { + return EC_POINT_get_affine_coordinates_GFp(m_Group, p, x, y, nullptr); + } - EC_POINT * GOSTR3410Curve::CreatePoint (const BIGNUM * x, const BIGNUM * y) const - { - EC_POINT * p = EC_POINT_new (m_Group); - EC_POINT_set_affine_coordinates_GFp (m_Group, p, x, y, nullptr); - return p; - } + EC_POINT *GOSTR3410Curve::CreatePoint(const BIGNUM *x, const BIGNUM *y) const { + EC_POINT *p = EC_POINT_new(m_Group); + EC_POINT_set_affine_coordinates_GFp(m_Group, p, x, y, nullptr); + return p; + } - void GOSTR3410Curve::Sign (const BIGNUM * priv, const BIGNUM * digest, BIGNUM * r, BIGNUM * s) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order(m_Group, q, ctx); - BIGNUM * k = BN_CTX_get (ctx); - BN_rand_range (k, q); // 0 < k < q - EC_POINT * C = MulP (k); // C = k*P - GetXY (C, r, nullptr); // r = Cx - EC_POINT_free (C); - BN_mod_mul (s, r, priv, q, ctx); // (r*priv)%q - BIGNUM * tmp = BN_CTX_get (ctx); - BN_mod_mul (tmp, k, digest, q, ctx); // (k*digest)%q - BN_mod_add (s, s, tmp, q, ctx); // (r*priv+k*digest)%q - BN_CTX_end (ctx); - BN_CTX_free (ctx); - } + void GOSTR3410Curve::Sign(const BIGNUM *priv, const BIGNUM *digest, BIGNUM *r, BIGNUM *s) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(m_Group, q, ctx); + BIGNUM *k = BN_CTX_get(ctx); + BN_rand_range(k, q); // 0 < k < q + EC_POINT *C = MulP(k); // C = k*P + GetXY(C, r, nullptr); // r = Cx + EC_POINT_free(C); + BN_mod_mul(s, r, priv, q, ctx); // (r*priv)%q + BIGNUM *tmp = BN_CTX_get(ctx); + BN_mod_mul(tmp, k, digest, q, ctx); // (k*digest)%q + BN_mod_add(s, s, tmp, q, ctx); // (r*priv+k*digest)%q + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } - bool GOSTR3410Curve::Verify (const EC_POINT * pub, const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s) - { - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order(m_Group, q, ctx); - BIGNUM * h = BN_CTX_get (ctx); - BN_mod (h, digest, q, ctx); // h = digest % q - BN_mod_inverse (h, h, q, ctx); // 1/h mod q - BIGNUM * z1 = BN_CTX_get (ctx); - BN_mod_mul (z1, s, h, q, ctx); // z1 = s/h - BIGNUM * z2 = BN_CTX_get (ctx); - BN_sub (z2, q, r); // z2 = -r - BN_mod_mul (z2, z2, h, q, ctx); // z2 = -r/h - EC_POINT * C = EC_POINT_new (m_Group); - EC_POINT_mul (m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub - BIGNUM * x = BN_CTX_get (ctx); - GetXY (C, x, nullptr); // Cx - BN_mod (x, x, q, ctx); // Cx % q - bool ret = !BN_cmp (x, r); // Cx = r ? - EC_POINT_free (C); - BN_CTX_end (ctx); - BN_CTX_free (ctx); - return ret; - } + bool GOSTR3410Curve::Verify(const EC_POINT *pub, const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s) { + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(m_Group, q, ctx); + BIGNUM *h = BN_CTX_get(ctx); + BN_mod(h, digest, q, ctx); // h = digest % q + BN_mod_inverse(h, h, q, ctx); // 1/h mod q + BIGNUM *z1 = BN_CTX_get(ctx); + BN_mod_mul(z1, s, h, q, ctx); // z1 = s/h + BIGNUM *z2 = BN_CTX_get(ctx); + BN_sub(z2, q, r); // z2 = -r + BN_mod_mul(z2, z2, h, q, ctx); // z2 = -r/h + EC_POINT *C = EC_POINT_new(m_Group); + EC_POINT_mul(m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub + BIGNUM *x = BN_CTX_get(ctx); + GetXY(C, x, nullptr); // Cx + BN_mod(x, x, q, ctx); // Cx % q + bool ret = !BN_cmp(x, r); // Cx = r ? + EC_POINT_free(C); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; + } - EC_POINT * GOSTR3410Curve::RecoverPublicKey (const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s, bool isNegativeY) const - { - // s*P = r*Q + h*C - BN_CTX * ctx = BN_CTX_new (); - BN_CTX_start (ctx); - EC_POINT * C = EC_POINT_new (m_Group); // C = k*P = (rx, ry) - EC_POINT * Q = nullptr; - if (EC_POINT_set_compressed_coordinates_GFp (m_Group, C, r, isNegativeY ? 1 : 0, ctx)) - { - EC_POINT * S = EC_POINT_new (m_Group); // S = s*P - EC_POINT_mul (m_Group, S, s, nullptr, nullptr, ctx); - BIGNUM * q = BN_CTX_get (ctx); - EC_GROUP_get_order(m_Group, q, ctx); - BIGNUM * h = BN_CTX_get (ctx); - BN_mod (h, digest, q, ctx); // h = digest % q - BN_sub (h, q, h); // h = -h - EC_POINT * H = EC_POINT_new (m_Group); - EC_POINT_mul (m_Group, H, nullptr, C, h, ctx); // -h*C - EC_POINT_add (m_Group, C, S, H, ctx); // s*P - h*C - EC_POINT_free (H); - EC_POINT_free (S); - BIGNUM * r1 = BN_CTX_get (ctx); - BN_mod_inverse (r1, r, q, ctx); - Q = EC_POINT_new (m_Group); - EC_POINT_mul (m_Group, Q, nullptr, C, r1, ctx); // (s*P - h*C)/r - } - EC_POINT_free (C); - BN_CTX_end (ctx); - BN_CTX_free (ctx); - return Q; - } + EC_POINT *GOSTR3410Curve::RecoverPublicKey(const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s, + bool isNegativeY) const { + // s*P = r*Q + h*C + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + EC_POINT *C = EC_POINT_new(m_Group); // C = k*P = (rx, ry) + EC_POINT *Q = nullptr; + if (EC_POINT_set_compressed_coordinates_GFp(m_Group, C, r, isNegativeY ? 1 : 0, ctx)) { + EC_POINT *S = EC_POINT_new(m_Group); // S = s*P + EC_POINT_mul(m_Group, S, s, nullptr, nullptr, ctx); + BIGNUM *q = BN_CTX_get(ctx); + EC_GROUP_get_order(m_Group, q, ctx); + BIGNUM *h = BN_CTX_get(ctx); + BN_mod(h, digest, q, ctx); // h = digest % q + BN_sub(h, q, h); // h = -h + EC_POINT *H = EC_POINT_new(m_Group); + EC_POINT_mul(m_Group, H, nullptr, C, h, ctx); // -h*C + EC_POINT_add(m_Group, C, S, H, ctx); // s*P - h*C + EC_POINT_free(H); + EC_POINT_free(S); + BIGNUM *r1 = BN_CTX_get(ctx); + BN_mod_inverse(r1, r, q, ctx); + Q = EC_POINT_new(m_Group); + EC_POINT_mul(m_Group, Q, nullptr, C, r1, ctx); // (s*P - h*C)/r + } + EC_POINT_free(C); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return Q; + } - static GOSTR3410Curve * CreateGOSTR3410Curve (GOSTR3410ParamSet paramSet) - { - // a, b, p, q, x, y - static const char * params[eGOSTR3410NumParamSets][6] = - { - { - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", - "A6", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893", - "1", - "8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14" - }, // CryptoPro A - { - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", - "E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275", - "3", - "7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4" - } // tc26-2012-paramSetA-512 - }; + static GOSTR3410Curve *CreateGOSTR3410Curve(GOSTR3410ParamSet paramSet) { + // a, b, p, q, x, y + static const char *params[eGOSTR3410NumParamSets][6] = + { + { + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", + "A6", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893", + "1", + "8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14" + }, // CryptoPro A + { + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", + "E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275", + "3", + "7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4" + } // tc26-2012-paramSetA-512 + }; - BIGNUM * a = nullptr, * b = nullptr, * p = nullptr, * q =nullptr, * x = nullptr, * y = nullptr; - BN_hex2bn(&a, params[paramSet][0]); - BN_hex2bn(&b, params[paramSet][1]); - BN_hex2bn(&p, params[paramSet][2]); - BN_hex2bn(&q, params[paramSet][3]); - BN_hex2bn(&x, params[paramSet][4]); - BN_hex2bn(&y, params[paramSet][5]); - auto curve = new GOSTR3410Curve (a, b, p, q, x, y); - BN_free (a); BN_free (b); BN_free (p); BN_free (q); BN_free (x); BN_free (y); - return curve; - } + BIGNUM *a = nullptr, *b = nullptr, *p = nullptr, *q = nullptr, *x = nullptr, *y = nullptr; + BN_hex2bn(&a, params[paramSet][0]); + BN_hex2bn(&b, params[paramSet][1]); + BN_hex2bn(&p, params[paramSet][2]); + BN_hex2bn(&q, params[paramSet][3]); + BN_hex2bn(&x, params[paramSet][4]); + BN_hex2bn(&y, params[paramSet][5]); + auto curve = new GOSTR3410Curve(a, b, p, q, x, y); + BN_free(a); + BN_free(b); + BN_free(p); + BN_free(q); + BN_free(x); + BN_free(y); + return curve; + } - static std::array, eGOSTR3410NumParamSets> g_GOSTR3410Curves; - std::unique_ptr& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet) - { - if (!g_GOSTR3410Curves[paramSet]) - { - auto c = CreateGOSTR3410Curve (paramSet); - if (!g_GOSTR3410Curves[paramSet]) // make sure it was not created already - g_GOSTR3410Curves[paramSet].reset (c); - else - delete c; - } - return g_GOSTR3410Curves[paramSet]; - } + static std::array , eGOSTR3410NumParamSets> g_GOSTR3410Curves; + + std::unique_ptr &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet) { + if (!g_GOSTR3410Curves[paramSet]) { + auto c = CreateGOSTR3410Curve(paramSet); + if (!g_GOSTR3410Curves[paramSet]) // make sure it was not created already + g_GOSTR3410Curves[paramSet].reset(c); + else + delete c; + } + return g_GOSTR3410Curves[paramSet]; + } // ГОСТ 34.11-2012 - static const uint64_t T0[256] = - { - 0xE6F87E5C5B711FD0, 0x258377800924FA16, 0xC849E07E852EA4A8, 0x5B4686A18F06C16A, - 0x0B32E9A2D77B416E, 0xABDA37A467815C66, 0xF61796A81A686676, 0xF5DC0B706391954B, - 0x4862F38DB7E64BF1, 0xFF5C629A68BD85C5, 0xCB827DA6FCD75795, 0x66D36DAF69B9F089, - 0x356C9F74483D83B0, 0x7CBCECB1238C99A1, 0x36A702AC31C4708D, 0x9EB6A8D02FBCDFD6, - 0x8B19FA51E5B3AE37, 0x9CCFB5408A127D0B, 0xBC0C78B508208F5A, 0xE533E3842288ECED, - 0xCEC2C7D377C15FD2, 0xEC7817B6505D0F5E, 0xB94CC2C08336871D, 0x8C205DB4CB0B04AD, - 0x763C855B28A0892F, 0x588D1B79F6FF3257, 0x3FECF69E4311933E, 0x0FC0D39F803A18C9, - 0xEE010A26F5F3AD83, 0x10EFE8F4411979A6, 0x5DCDA10C7DE93A10, 0x4A1BEE1D1248E92C, - 0x53BFF2DB21847339, 0xB4F50CCFA6A23D09, 0x5FB4BC9CD84798CD, 0xE88A2D8B071C56F9, - 0x7F7771695A756A9C, 0xC5F02E71A0BA1EBC, 0xA663F9AB4215E672, 0x2EB19E22DE5FBB78, - 0x0DB9CE0F2594BA14, 0x82520E6397664D84, 0x2F031E6A0208EA98, 0x5C7F2144A1BE6BF0, - 0x7A37CB1CD16362DB, 0x83E08E2B4B311C64, 0xCF70479BAB960E32, 0x856BA986B9DEE71E, - 0xB5478C877AF56CE9, 0xB8FE42885F61D6FD, 0x1BDD0156966238C8, 0x622157923EF8A92E, - 0xFC97FF42114476F8, 0x9D7D350856452CEB, 0x4C90C9B0E0A71256, 0x2308502DFBCB016C, - 0x2D7A03FAA7A64845, 0xF46E8B38BFC6C4AB, 0xBDBEF8FDD477DEBA, 0x3AAC4CEBC8079B79, - 0xF09CB105E8879D0C, 0x27FA6A10AC8A58CB, 0x8960E7C1401D0CEA, 0x1A6F811E4A356928, - 0x90C4FB0773D196FF, 0x43501A2F609D0A9F, 0xF7A516E0C63F3796, 0x1CE4A6B3B8DA9252, - 0x1324752C38E08A9B, 0xA5A864733BEC154F, 0x2BF124575549B33F, 0xD766DB15440DC5C7, - 0xA7D179E39E42B792, 0xDADF151A61997FD3, 0x86A0345EC0271423, 0x38D5517B6DA939A4, - 0x6518F077104003B4, 0x02791D90A5AEA2DD, 0x88D267899C4A5D0A, 0x930F66DF0A2865C2, - 0x4EE9D4204509B08B, 0x325538916685292A, 0x412907BFC533A842, 0xB27E2B62544DC673, - 0x6C5304456295E007, 0x5AF406E95351908A, 0x1F2F3B6BC123616F, 0xC37B09DC5255E5C6, - 0x3967D133B1FE6844, 0x298839C7F0E711E2, 0x409B87F71964F9A2, 0xE938ADC3DB4B0719, - 0x0C0B4E47F9C3EBF4, 0x5534D576D36B8843, 0x4610A05AEB8B02D8, 0x20C3CDF58232F251, - 0x6DE1840DBEC2B1E7, 0xA0E8DE06B0FA1D08, 0x7B854B540D34333B, 0x42E29A67BCCA5B7F, - 0xD8A6088AC437DD0E, 0xC63BB3A9D943ED81, 0x21714DBD5E65A3B1, 0x6761EDE7B5EEA169, - 0x2431F7C8D573ABF6, 0xD51FC685E1A3671A, 0x5E063CD40410C92D, 0x283AB98F2CB04002, - 0x8FEBC06CB2F2F790, 0x17D64F116FA1D33C, 0xE07359F1A99EE4AA, 0x784ED68C74CDC006, - 0x6E2A19D5C73B42DA, 0x8712B4161C7045C3, 0x371582E4ED93216D, 0xACE390414939F6FC, - 0x7EC5F12186223B7C, 0xC0B094042BAC16FB, 0xF9D745379A527EBF, 0x737C3F2EA3B68168, - 0x33E7B8D9BAD278CA, 0xA9A32A34C22FFEBB, 0xE48163CCFEDFBD0D, 0x8E5940246EA5A670, - 0x51C6EF4B842AD1E4, 0x22BAD065279C508C, 0xD91488C218608CEE, 0x319EA5491F7CDA17, - 0xD394E128134C9C60, 0x094BF43272D5E3B3, 0x9BF612A5A4AAD791, 0xCCBBDA43D26FFD0F, - 0x34DE1F3C946AD250, 0x4F5B5468995EE16B, 0xDF9FAF6FEA8F7794, 0x2648EA5870DD092B, - 0xBFC7E56D71D97C67, 0xDDE6B2FF4F21D549, 0x3C276B463AE86003, 0x91767B4FAF86C71F, - 0x68A13E7835D4B9A0, 0xB68C115F030C9FD4, 0x141DD2C916582001, 0x983D8F7DDD5324AC, - 0x64AA703FCC175254, 0xC2C989948E02B426, 0x3E5E76D69F46C2DE, 0x50746F03587D8004, - 0x45DB3D829272F1E5, 0x60584A029B560BF3, 0xFBAE58A73FFCDC62, 0xA15A5E4E6CAD4CE8, - 0x4BA96E55CE1FB8CC, 0x08F9747AAE82B253, 0xC102144CF7FB471B, 0x9F042898F3EB8E36, - 0x068B27ADF2EFFB7A, 0xEDCA97FE8C0A5EBE, 0x778E0513F4F7D8CF, 0x302C2501C32B8BF7, - 0x8D92DDFC175C554D, 0xF865C57F46052F5F, 0xEAF3301BA2B2F424, 0xAA68B7ECBBD60D86, - 0x998F0F350104754C, 0x0000000000000000, 0xF12E314D34D0CCEC, 0x710522BE061823B5, - 0xAF280D9930C005C1, 0x97FD5CE25D693C65, 0x19A41CC633CC9A15, 0x95844172F8C79EB8, - 0xDC5432B7937684A9, 0x9436C13A2490CF58, 0x802B13F332C8EF59, 0xC442AE397CED4F5C, - 0xFA1CD8EFE3AB8D82, 0xF2E5AC954D293FD1, 0x6AD823E8907A1B7D, 0x4D2249F83CF043B6, - 0x03CB9DD879F9F33D, 0xDE2D2F2736D82674, 0x2A43A41F891EE2DF, 0x6F98999D1B6C133A, - 0xD4AD46CD3DF436FA, 0xBB35DF50269825C0, 0x964FDCAA813E6D85, 0xEB41B0537EE5A5C4, - 0x0540BA758B160847, 0xA41AE43BE7BB44AF, 0xE3B8C429D0671797, 0x819993BBEE9FBEB9, - 0xAE9A8DD1EC975421, 0xF3572CDD917E6E31, 0x6393D7DAE2AFF8CE, 0x47A2201237DC5338, - 0xA32343DEC903EE35, 0x79FC56C4A89A91E6, 0x01B28048DC5751E0, 0x1296F564E4B7DB7B, - 0x75F7188351597A12, 0xDB6D9552BDCE2E33, 0x1E9DBB231D74308F, 0x520D7293FDD322D9, - 0xE20A44610C304677, 0xFEEEE2D2B4EAD425, 0xCA30FDEE20800675, 0x61EACA4A47015A13, - 0xE74AFE1487264E30, 0x2CC883B27BF119A5, 0x1664CF59B3F682DC, 0xA811AA7C1E78AF5B, - 0x1D5626FB648DC3B2, 0xB73E9117DF5BCE34, 0xD05F7CF06AB56F5D, 0xFD257F0ACD132718, - 0x574DC8E676C52A9E, 0x0739A7E52EB8AA9A, 0x5486553E0F3CD9A3, 0x56FF48AEAA927B7E, - 0xBE756525AD8E2D87, 0x7D0E6CF9FFDBC841, 0x3B1ECCA31450CA99, 0x6913BE30E983E840, - 0xAD511009956EA71C, 0xB1B5B6BA2DB4354E, 0x4469BDCA4E25A005, 0x15AF5281CA0F71E1, - 0x744598CB8D0E2BF2, 0x593F9B312AA863B7, 0xEFB38A6E29A4FC63, 0x6B6AA3A04C2D4A9D, - 0x3D95EB0EE6BF31E3, 0xA291C3961554BFD5, 0x18169C8EEF9BCBF5, 0x115D68BC9D4E2846, - 0xBA875F18FACF7420, 0xD1EDFCB8B6E23EBD, 0xB00736F2F1E364AE, 0x84D929CE6589B6FE, - 0x70B7A2F6DA4F7255, 0x0E7253D75C6D4929, 0x04F23A3D574159A7, 0x0A8069EA0B2C108E, - 0x49D073C56BB11A11, 0x8AAB7A1939E4FFD7, 0xCD095A0B0E38ACEF, 0xC9FB60365979F548, - 0x92BDE697D67F3422, 0xC78933E10514BC61, 0xE1C1D9B975C9B54A, 0xD2266160CF1BCD80, - 0x9A4492ED78FD8671, 0xB3CCAB2A881A9793, 0x72CEBF667FE1D088, 0xD6D45B5D985A9427 - }; - static const uint64_t T1[256] = - { - 0xC811A8058C3F55DE, 0x65F5B43196B50619, 0xF74F96B1D6706E43, 0x859D1E8BCB43D336, - 0x5AAB8A85CCFA3D84, 0xF9C7BF99C295FCFD, 0xA21FD5A1DE4B630F, 0xCDB3EF763B8B456D, - 0x803F59F87CF7C385, 0xB27C73BE5F31913C, 0x98E3AC6633B04821, 0xBF61674C26B8F818, - 0x0FFBC995C4C130C8, 0xAAA0862010761A98, 0x6057F342210116AA, 0xF63C760C0654CC35, - 0x2DDB45CC667D9042, 0xBCF45A964BD40382, 0x68E8A0C3EF3C6F3D, 0xA7BD92D269FF73BC, - 0x290AE20201ED2287, 0xB7DE34CDE885818F, 0xD901EEA7DD61059B, 0xD6FA273219A03553, - 0xD56F1AE874CCCEC9, 0xEA31245C2E83F554, 0x7034555DA07BE499, 0xCE26D2AC56E7BEF7, - 0xFD161857A5054E38, 0x6A0E7DA4527436D1, 0x5BD86A381CDE9FF2, 0xCAF7756231770C32, - 0xB09AAED9E279C8D0, 0x5DEF1091C60674DB, 0x111046A2515E5045, 0x23536CE4729802FC, - 0xC50CBCF7F5B63CFA, 0x73A16887CD171F03, 0x7D2941AFD9F28DBD, 0x3F5E3EB45A4F3B9D, - 0x84EEFE361B677140, 0x3DB8E3D3E7076271, 0x1A3A28F9F20FD248, 0x7EBC7C75B49E7627, - 0x74E5F293C7EB565C, 0x18DCF59E4F478BA4, 0x0C6EF44FA9ADCB52, 0xC699812D98DAC760, - 0x788B06DC6E469D0E, 0xFC65F8EA7521EC4E, 0x30A5F7219E8E0B55, 0x2BEC3F65BCA57B6B, - 0xDDD04969BAF1B75E, 0x99904CDBE394EA57, 0x14B201D1E6EA40F6, 0xBBB0C08241284ADD, - 0x50F20463BF8F1DFF, 0xE8D7F93B93CBACB8, 0x4D8CB68E477C86E8, 0xC1DD1B3992268E3F, - 0x7C5AA11209D62FCB, 0x2F3D98ABDB35C9AE, 0x671369562BFD5FF5, 0x15C1E16C36CEE280, - 0x1D7EB2EDF8F39B17, 0xDA94D37DB00DFE01, 0x877BC3EC760B8ADA, 0xCB8495DFE153AE44, - 0x05A24773B7B410B3, 0x12857B783C32ABDF, 0x8EB770D06812513B, 0x536739B9D2E3E665, - 0x584D57E271B26468, 0xD789C78FC9849725, 0xA935BBFA7D1AE102, 0x8B1537A3DFA64188, - 0xD0CD5D9BC378DE7A, 0x4AC82C9A4D80CFB7, 0x42777F1B83BDB620, 0x72D2883A1D33BD75, - 0x5E7A2D4BAB6A8F41, 0xF4DAAB6BBB1C95D9, 0x905CFFE7FD8D31B6, 0x83AA6422119B381F, - 0xC0AEFB8442022C49, 0xA0F908C663033AE3, 0xA428AF0804938826, 0xADE41C341A8A53C7, - 0xAE7121EE77E6A85D, 0xC47F5C4A25929E8C, 0xB538E9AA55CDD863, 0x06377AA9DAD8EB29, - 0xA18AE87BB3279895, 0x6EDFDA6A35E48414, 0x6B7D9D19825094A7, 0xD41CFA55A4E86CBF, - 0xE5CAEDC9EA42C59C, 0xA36C351C0E6FC179, 0x5181E4DE6FABBF89, 0xFFF0C530184D17D4, - 0x9D41EB1584045892, 0x1C0D525028D73961, 0xF178EC180CA8856A, 0x9A0571018EF811CD, - 0x4091A27C3EF5EFCC, 0x19AF15239F6329D2, 0x347450EFF91EB990, 0xE11B4A078DD27759, - 0xB9561DE5FC601331, 0x912F1F5A2DA993C0, 0x1654DCB65BA2191A, 0x3E2DDE098A6B99EB, - 0x8A66D71E0F82E3FE, 0x8C51ADB7D55A08D7, 0x4533E50F8941FF7F, 0x02E6DD67BD4859EC, - 0xE068AABA5DF6D52F, 0xC24826E3FF4A75A5, 0x6C39070D88ACDDF8, 0x6486548C4691A46F, - 0xD1BEBD26135C7C0C, 0xB30F93038F15334A, 0x82D9849FC1BF9A69, 0x9C320BA85420FAE4, - 0xFA528243AFF90767, 0x9ED4D6CFE968A308, 0xB825FD582C44B147, 0x9B7691BC5EDCB3BB, - 0xC7EA619048FE6516, 0x1063A61F817AF233, 0x47D538683409A693, 0x63C2CE984C6DED30, - 0x2A9FDFD86C81D91D, 0x7B1E3B06032A6694, 0x666089EBFBD9FD83, 0x0A598EE67375207B, - 0x07449A140AFC495F, 0x2CA8A571B6593234, 0x1F986F8A45BBC2FB, 0x381AA4A050B372C2, - 0x5423A3ADD81FAF3A, 0x17273C0B8B86BB6C, 0xFE83258DC869B5A2, 0x287902BFD1C980F1, - 0xF5A94BD66B3837AF, 0x88800A79B2CABA12, 0x55504310083B0D4C, 0xDF36940E07B9EEB2, - 0x04D1A7CE6790B2C5, 0x612413FFF125B4DC, 0x26F12B97C52C124F, 0x86082351A62F28AC, - 0xEF93632F9937E5E7, 0x3507B052293A1BE6, 0xE72C30AE570A9C70, 0xD3586041AE1425E0, - 0xDE4574B3D79D4CC4, 0x92BA228040C5685A, 0xF00B0CA5DC8C271C, 0xBE1287F1F69C5A6E, - 0xF39E317FB1E0DC86, 0x495D114020EC342D, 0x699B407E3F18CD4B, 0xDCA3A9D46AD51528, - 0x0D1D14F279896924, 0x0000000000000000, 0x593EB75FA196C61E, 0x2E4E78160B116BD8, - 0x6D4AE7B058887F8E, 0xE65FD013872E3E06, 0x7A6DDBBBD30EC4E2, 0xAC97FC89CAAEF1B1, - 0x09CCB33C1E19DBE1, 0x89F3EAC462EE1864, 0x7770CF49AA87ADC6, 0x56C57ECA6557F6D6, - 0x03953DDA6D6CFB9A, 0x36928D884456E07C, 0x1EEB8F37959F608D, 0x31D6179C4EAAA923, - 0x6FAC3AD7E5C02662, 0x43049FA653991456, 0xABD3669DC052B8EE, 0xAF02C153A7C20A2B, - 0x3CCB036E3723C007, 0x93C9C23D90E1CA2C, 0xC33BC65E2F6ED7D3, 0x4CFF56339758249E, - 0xB1E94E64325D6AA6, 0x37E16D359472420A, 0x79F8E661BE623F78, 0x5214D90402C74413, - 0x482EF1FDF0C8965B, 0x13F69BC5EC1609A9, 0x0E88292814E592BE, 0x4E198B542A107D72, - 0xCCC00FCBEBAFE71B, 0x1B49C844222B703E, 0x2564164DA840E9D5, 0x20C6513E1FF4F966, - 0xBAC3203F910CE8AB, 0xF2EDD1C261C47EF0, 0x814CB945ACD361F3, 0x95FEB8944A392105, - 0x5C9CF02C1622D6AD, 0x971865F3F77178E9, 0xBD87BA2B9BF0A1F4, 0x444005B259655D09, - 0xED75BE48247FBC0B, 0x7596122E17CFF42A, 0xB44B091785E97A15, 0x966B854E2755DA9F, - 0xEEE0839249134791, 0x32432A4623C652B9, 0xA8465B47AD3E4374, 0xF8B45F2412B15E8B, - 0x2417F6F078644BA3, 0xFB2162FE7FDDA511, 0x4BBBCC279DA46DC1, 0x0173E0BDD024A276, - 0x22208C59A2BCA08A, 0x8FC4906DB836F34D, 0xE4B90D743A6667EA, 0x7147B5E0705F46EF, - 0x2782CB2A1508B039, 0xEC065EF5F45B1E7D, 0x21B5B183CFD05B10, 0xDBE733C060295C77, - 0x9FA73672394C017E, 0xCF55321186C31C81, 0xD8720E1A0D45A7ED, 0x3B8F997A3DDF8958, - 0x3AFC79C7EDFB2B2E, 0xE9A4198643EF0ECE, 0x5F09CDF67B4E2D37, 0x4F6A6BE9FA34DF04, - 0xB6ADD47038A123F9, 0x8D224D0A057EAAA1, 0xC96248B85C1BF7A8, 0xE3FD9760309A2EB5, - 0x0B2A6E5BA351820D, 0xEB42C4E1FEA75722, 0x948D58299A1D8373, 0x7FCF9CC864BAD451, - 0xA55B4FB5D4B72A50, 0x08BF5381CE3D7997, 0x46A6D8D5E42D04E5, 0xD22B80FC7E308796, - 0x57B69E77B57354A0, 0x3969441D8097D0B4, 0x3330CAFBF3E2F0CF, 0xE28E77DDE0BE8CC3, - 0x62B12E259C494F46, 0xA6CE726FB9DBD1CA, 0x41E242C1EED14DBA, 0x76032FF47AA30FB0 - }; - static const uint64_t T2[256] = - { - 0x45B268A93ACDE4CC, 0xAF7F0BE884549D08, 0x048354B3C1468263, 0x925435C2C80EFED2, - 0xEE4E37F27FDFFBA7, 0x167A33920C60F14D, 0xFB123B52EA03E584, 0x4A0CAB53FDBB9007, - 0x9DEAF6380F788A19, 0xCB48EC558F0CB32A, 0xB59DC4B2D6FEF7E0, 0xDCDBCA22F4F3ECB6, - 0x11DF5813549A9C40, 0xE33FDEDF568ACED3, 0xA0C1C8124322E9C3, 0x07A56B8158FA6D0D, - 0x77279579B1E1F3DD, 0xD9B18B74422AC004, 0xB8EC2D9FFFABC294, 0xF4ACF8A82D75914F, - 0x7BBF69B1EF2B6878, 0xC4F62FAF487AC7E1, 0x76CE809CC67E5D0C, 0x6711D88F92E4C14C, - 0x627B99D9243DEDFE, 0x234AA5C3DFB68B51, 0x909B1F15262DBF6D, 0x4F66EA054B62BCB5, - 0x1AE2CF5A52AA6AE8, 0xBEA053FBD0CE0148, 0xED6808C0E66314C9, 0x43FE16CD15A82710, - 0xCD049231A06970F6, 0xE7BC8A6C97CC4CB0, 0x337CE835FCB3B9C0, 0x65DEF2587CC780F3, - 0x52214EDE4132BB50, 0x95F15E4390F493DF, 0x870839625DD2E0F1, 0x41313C1AFB8B66AF, - 0x91720AF051B211BC, 0x477D427ED4EEA573, 0x2E3B4CEEF6E3BE25, 0x82627834EB0BCC43, - 0x9C03E3DD78E724C8, 0x2877328AD9867DF9, 0x14B51945E243B0F2, 0x574B0F88F7EB97E2, - 0x88B6FA989AA4943A, 0x19C4F068CB168586, 0x50EE6409AF11FAEF, 0x7DF317D5C04EABA4, - 0x7A567C5498B4C6A9, 0xB6BBFB804F42188E, 0x3CC22BCF3BC5CD0B, 0xD04336EAAA397713, - 0xF02FAC1BEC33132C, 0x2506DBA7F0D3488D, 0xD7E65D6BF2C31A1E, 0x5EB9B2161FF820F5, - 0x842E0650C46E0F9F, 0x716BEB1D9E843001, 0xA933758CAB315ED4, 0x3FE414FDA2792265, - 0x27C9F1701EF00932, 0x73A4C1CA70A771BE, 0x94184BA6E76B3D0E, 0x40D829FF8C14C87E, - 0x0FBEC3FAC77674CB, 0x3616A9634A6A9572, 0x8F139119C25EF937, 0xF545ED4D5AEA3F9E, - 0xE802499650BA387B, 0x6437E7BD0B582E22, 0xE6559F89E053E261, 0x80AD52E305288DFC, - 0x6DC55A23E34B9935, 0xDE14E0F51AD0AD09, 0xC6390578A659865E, 0x96D7617109487CB1, - 0xE2D6CB3A21156002, 0x01E915E5779FAED1, 0xADB0213F6A77DCB7, 0x9880B76EB9A1A6AB, - 0x5D9F8D248644CF9B, 0xFD5E4536C5662658, 0xF1C6B9FE9BACBDFD, 0xEACD6341BE9979C4, - 0xEFA7221708405576, 0x510771ECD88E543E, 0xC2BA51CB671F043D, 0x0AD482AC71AF5879, - 0xFE787A045CDAC936, 0xB238AF338E049AED, 0xBD866CC94972EE26, 0x615DA6EBBD810290, - 0x3295FDD08B2C1711, 0xF834046073BF0AEA, 0xF3099329758FFC42, 0x1CAEB13E7DCFA934, - 0xBA2307481188832B, 0x24EFCE42874CE65C, 0x0E57D61FB0E9DA1A, 0xB3D1BAD6F99B343C, - 0xC0757B1C893C4582, 0x2B510DB8403A9297, 0x5C7698C1F1DB614A, 0x3E0D0118D5E68CB4, - 0xD60F488E855CB4CF, 0xAE961E0DF3CB33D9, 0x3A8E55AB14A00ED7, 0x42170328623789C1, - 0x838B6DD19C946292, 0x895FEF7DED3B3AEB, 0xCFCBB8E64E4A3149, 0x064C7E642F65C3DC, - 0x3D2B3E2A4C5A63DA, 0x5BD3F340A9210C47, 0xB474D157A1615931, 0xAC5934DA1DE87266, - 0x6EE365117AF7765B, 0xC86ED36716B05C44, 0x9BA6885C201D49C5, 0xB905387A88346C45, - 0x131072C4BAB9DDFF, 0xBF49461EA751AF99, 0xD52977BC1CE05BA1, 0xB0F785E46027DB52, - 0x546D30BA6E57788C, 0x305AD707650F56AE, 0xC987C682612FF295, 0xA5AB8944F5FBC571, - 0x7ED528E759F244CA, 0x8DDCBBCE2C7DB888, 0xAA154ABE328DB1BA, 0x1E619BE993ECE88B, - 0x09F2BD9EE813B717, 0x7401AA4B285D1CB3, 0x21858F143195CAEE, 0x48C381841398D1B8, - 0xFCB750D3B2F98889, 0x39A86A998D1CE1B9, 0x1F888E0CE473465A, 0x7899568376978716, - 0x02CF2AD7EE2341BF, 0x85C713B5B3F1A14E, 0xFF916FE12B4567E7, 0x7C1A0230B7D10575, - 0x0C98FCC85ECA9BA5, 0xA3E7F720DA9E06AD, 0x6A6031A2BBB1F438, 0x973E74947ED7D260, - 0x2CF4663918C0FF9A, 0x5F50A7F368678E24, 0x34D983B4A449D4CD, 0x68AF1B755592B587, - 0x7F3C3D022E6DEA1B, 0xABFC5F5B45121F6B, 0x0D71E92D29553574, 0xDFFDF5106D4F03D8, - 0x081BA87B9F8C19C6, 0xDB7EA1A3AC0981BB, 0xBBCA12AD66172DFA, 0x79704366010829C7, - 0x179326777BFF5F9C, 0x0000000000000000, 0xEB2476A4C906D715, 0x724DD42F0738DF6F, - 0xB752EE6538DDB65F, 0x37FFBC863DF53BA3, 0x8EFA84FCB5C157E6, 0xE9EB5C73272596AA, - 0x1B0BDABF2535C439, 0x86E12C872A4D4E20, 0x9969A28BCE3E087A, 0xFAFB2EB79D9C4B55, - 0x056A4156B6D92CB2, 0x5A3AE6A5DEBEA296, 0x22A3B026A8292580, 0x53C85B3B36AD1581, - 0xB11E900117B87583, 0xC51F3A4A3FE56930, 0xE019E1EDCF3621BD, 0xEC811D2591FCBA18, - 0x445B7D4C4D524A1D, 0xA8DA6069DCAEF005, 0x58F5CC72309DE329, 0xD4C062596B7FF570, - 0xCE22AD0339D59F98, 0x591CD99747024DF8, 0x8B90C5AA03187B54, 0xF663D27FC356D0F0, - 0xD8589E9135B56ED5, 0x35309651D3D67A1C, 0x12F96721CD26732E, 0xD28C1C3D441A36AC, - 0x492A946164077F69, 0x2D1D73DC6F5F514B, 0x6F0A70F40D68D88A, 0x60B4B30ECA1EAC41, - 0xD36509D83385987D, 0x0B3D97490630F6A8, 0x9ECCC90A96C46577, 0xA20EE2C5AD01A87C, - 0xE49AB55E0E70A3DE, 0xA4429CA182646BA0, 0xDA97B446DB962F6A, 0xCCED87D4D7F6DE27, - 0x2AB8185D37A53C46, 0x9F25DCEFE15BCBA6, 0xC19C6EF9FEA3EB53, 0xA764A3931BD884CE, - 0x2FD2590B817C10F4, 0x56A21A6D80743933, 0xE573A0BB79EF0D0F, 0x155C0CA095DC1E23, - 0x6C2C4FC694D437E4, 0x10364DF623053291, 0xDD32DFC7836C4267, 0x03263F3299BCEF6E, - 0x66F8CD6AE57B6F9D, 0x8C35AE2B5BE21659, 0x31B3C2E21290F87F, 0x93BD2027BF915003, - 0x69460E90220D1B56, 0x299E276FAE19D328, 0x63928C3C53A2432F, 0x7082FEF8E91B9ED0, - 0xBC6F792C3EED40F7, 0x4C40D537D2DE53DB, 0x75E8BFAE5FC2B262, 0x4DA9C0D2A541FD0A, - 0x4E8FFFE03CFD1264, 0x2620E495696FA7E3, 0xE1F0F408B8A98F6C, 0xD1AA230FDDA6D9C2, - 0xC7D0109DD1C6288F, 0x8A79D04F7487D585, 0x4694579BA3710BA2, 0x38417F7CFA834F68, - 0x1D47A4DB0A5007E5, 0x206C9AF1460A643F, 0xA128DDF734BD4712, 0x8144470672B7232D, - 0xF2E086CC02105293, 0x182DE58DBC892B57, 0xCAA1F9B0F8931DFB, 0x6B892447CC2E5AE9, - 0xF9DD11850420A43B, 0x4BE5BEB68A243ED6, 0x5584255F19C8D65D, 0x3B67404E633FA006, - 0xA68DB6766C472A1F, 0xF78AC79AB4C97E21, 0xC353442E1080AAEC, 0x9A4F9DB95782E714 - }; - static const uint64_t T3[256] = - { - 0x05BA7BC82C9B3220, 0x31A54665F8B65E4F, 0xB1B651F77547F4D4, 0x8BFA0D857BA46682, - 0x85A96C5AA16A98BB, 0x990FAEF908EB79C9, 0xA15E37A247F4A62D, 0x76857DCD5D27741E, - 0xF8C50B800A1820BC, 0xBE65DCB201F7A2B4, 0x666D1B986F9426E7, 0x4CC921BF53C4E648, - 0x95410A0F93D9CA42, 0x20CDCCAA647BA4EF, 0x429A4060890A1871, 0x0C4EA4F69B32B38B, - 0xCCDA362DDE354CD3, 0x96DC23BC7C5B2FA9, 0xC309BB68AA851AB3, 0xD26131A73648E013, - 0x021DC52941FC4DB2, 0xCD5ADAB7704BE48A, 0xA77965D984ED71E6, 0x32386FD61734BBA4, - 0xE82D6DD538AB7245, 0x5C2147EA6177B4B1, 0x5DA1AB70CF091CE8, 0xAC907FCE72B8BDFF, - 0x57C85DFD972278A8, 0xA4E44C6A6B6F940D, 0x3851995B4F1FDFE4, 0x62578CCAED71BC9E, - 0xD9882BB0C01D2C0A, 0x917B9D5D113C503B, 0xA2C31E11A87643C6, 0xE463C923A399C1CE, - 0xF71686C57EA876DC, 0x87B4A973E096D509, 0xAF0D567D9D3A5814, 0xB40C2A3F59DCC6F4, - 0x3602F88495D121DD, 0xD3E1DD3D9836484A, 0xF945E71AA46688E5, 0x7518547EB2A591F5, - 0x9366587450C01D89, 0x9EA81018658C065B, 0x4F54080CBC4603A3, 0x2D0384C65137BF3D, - 0xDC325078EC861E2A, 0xEA30A8FC79573FF7, 0x214D2030CA050CB6, 0x65F0322B8016C30C, - 0x69BE96DD1B247087, 0xDB95EE9981E161B8, 0xD1FC1814D9CA05F8, 0x820ED2BBCC0DE729, - 0x63D76050430F14C7, 0x3BCCB0E8A09D3A0F, 0x8E40764D573F54A2, 0x39D175C1E16177BD, - 0x12F5A37C734F1F4B, 0xAB37C12F1FDFC26D, 0x5648B167395CD0F1, 0x6C04ED1537BF42A7, - 0xED97161D14304065, 0x7D6C67DAAB72B807, 0xEC17FA87BA4EE83C, 0xDFAF79CB0304FBC1, - 0x733F060571BC463E, 0x78D61C1287E98A27, 0xD07CF48E77B4ADA1, 0xB9C262536C90DD26, - 0xE2449B5860801605, 0x8FC09AD7F941FCFB, 0xFAD8CEA94BE46D0E, 0xA343F28B0608EB9F, - 0x9B126BD04917347B, 0x9A92874AE7699C22, 0x1B017C42C4E69EE0, 0x3A4C5C720EE39256, - 0x4B6E9F5E3EA399DA, 0x6BA353F45AD83D35, 0xE7FEE0904C1B2425, 0x22D009832587E95D, - 0x842980C00F1430E2, 0xC6B3C0A0861E2893, 0x087433A419D729F2, 0x341F3DADD42D6C6F, - 0xEE0A3FAEFBB2A58E, 0x4AEE73C490DD3183, 0xAAB72DB5B1A16A34, 0xA92A04065E238FDF, - 0x7B4B35A1686B6FCC, 0x6A23BF6EF4A6956C, 0x191CB96B851AD352, 0x55D598D4D6DE351A, - 0xC9604DE5F2AE7EF3, 0x1CA6C2A3A981E172, 0xDE2F9551AD7A5398, 0x3025AAFF56C8F616, - 0x15521D9D1E2860D9, 0x506FE31CFA45073A, 0x189C55F12B647B0B, 0x0180EC9AAE7EA859, - 0x7CEC8B40050C105E, 0x2350E5198BF94104, 0xEF8AD33455CC0DD7, 0x07A7BEE16D677F92, - 0xE5E325B90DE76997, 0x5A061591A26E637A, 0xB611EF1618208B46, 0x09F4DF3EB7A981AB, - 0x1EBB078AE87DACC0, 0xB791038CB65E231F, 0x0FD38D4574B05660, 0x67EDF702C1EA8EBE, - 0xBA5F4BE0831238CD, 0xE3C477C2CEFEBE5C, 0x0DCE486C354C1BD2, 0x8C5DB36416C31910, - 0x26EA9ED1A7627324, 0x039D29B3EF82E5EB, 0x9F28FC82CBF2AE02, 0xA8AAE89CF05D2786, - 0x431AACFA2774B028, 0xCF471F9E31B7A938, 0x581BD0B8E3922EC8, 0xBC78199B400BEF06, - 0x90FB71C7BF42F862, 0x1F3BEB1046030499, 0x683E7A47B55AD8DE, 0x988F4263A695D190, - 0xD808C72A6E638453, 0x0627527BC319D7CB, 0xEBB04466D72997AE, 0xE67E0C0AE2658C7C, - 0x14D2F107B056C880, 0x7122C32C30400B8C, 0x8A7AE11FD5DACEDB, 0xA0DEDB38E98A0E74, - 0xAD109354DCC615A6, 0x0BE91A17F655CC19, 0x8DDD5FFEB8BDB149, 0xBFE53028AF890AED, - 0xD65BA6F5B4AD7A6A, 0x7956F0882997227E, 0x10E8665532B352F9, 0x0E5361DFDACEFE39, - 0xCEC7F3049FC90161, 0xFF62B561677F5F2E, 0x975CCF26D22587F0, 0x51EF0F86543BAF63, - 0x2F1E41EF10CBF28F, 0x52722635BBB94A88, 0xAE8DBAE73344F04D, 0x410769D36688FD9A, - 0xB3AB94DE34BBB966, 0x801317928DF1AA9B, 0xA564A0F0C5113C54, 0xF131D4BEBDB1A117, - 0x7F71A2F3EA8EF5B5, 0x40878549C8F655C3, 0x7EF14E6944F05DEC, 0xD44663DCF55137D8, - 0xF2ACFD0D523344FC, 0x0000000000000000, 0x5FBC6E598EF5515A, 0x16CF342EF1AA8532, - 0xB036BD6DDB395C8D, 0x13754FE6DD31B712, 0xBBDFA77A2D6C9094, 0x89E7C8AC3A582B30, - 0x3C6B0E09CDFA459D, 0xC4AE0589C7E26521, 0x49735A777F5FD468, 0xCAFD64561D2C9B18, - 0xDA1502032F9FC9E1, 0x8867243694268369, 0x3782141E3BAF8984, 0x9CB5D53124704BE9, - 0xD7DB4A6F1AD3D233, 0xA6F989432A93D9BF, 0x9D3539AB8A0EE3B0, 0x53F2CAAF15C7E2D1, - 0x6E19283C76430F15, 0x3DEBE2936384EDC4, 0x5E3C82C3208BF903, 0x33B8834CB94A13FD, - 0x6470DEB12E686B55, 0x359FD1377A53C436, 0x61CAA57902F35975, 0x043A975282E59A79, - 0xFD7F70482683129C, 0xC52EE913699CCD78, 0x28B9FF0E7DAC8D1D, 0x5455744E78A09D43, - 0xCB7D88CCB3523341, 0x44BD121B4A13CFBA, 0x4D49CD25FDBA4E11, 0x3E76CB208C06082F, - 0x3FF627BA2278A076, 0xC28957F204FBB2EA, 0x453DFE81E46D67E3, 0x94C1E6953DA7621B, - 0x2C83685CFF491764, 0xF32C1197FC4DECA5, 0x2B24D6BD922E68F6, 0xB22B78449AC5113F, - 0x48F3B6EDD1217C31, 0x2E9EAD75BEB55AD6, 0x174FD8B45FD42D6B, 0x4ED4E4961238ABFA, - 0x92E6B4EEFEBEB5D0, 0x46A0D7320BEF8208, 0x47203BA8A5912A51, 0x24F75BF8E69E3E96, - 0xF0B1382413CF094E, 0xFEE259FBC901F777, 0x276A724B091CDB7D, 0xBDF8F501EE75475F, - 0x599B3C224DEC8691, 0x6D84018F99C1EAFE, 0x7498B8E41CDB39AC, 0xE0595E71217C5BB7, - 0x2AA43A273C50C0AF, 0xF50B43EC3F543B6E, 0x838E3E2162734F70, 0xC09492DB4507FF58, - 0x72BFEA9FDFC2EE67, 0x11688ACF9CCDFAA0, 0x1A8190D86A9836B9, 0x7ACBD93BC615C795, - 0xC7332C3A286080CA, 0x863445E94EE87D50, 0xF6966A5FD0D6DE85, 0xE9AD814F96D5DA1C, - 0x70A22FB69E3EA3D5, 0x0A69F68D582B6440, 0xB8428EC9C2EE757F, 0x604A49E3AC8DF12C, - 0x5B86F90B0C10CB23, 0xE1D9B2EB8F02F3EE, 0x29391394D3D22544, 0xC8E0A17F5CD0D6AA, - 0xB58CC6A5F7A26EAD, 0x8193FB08238F02C2, 0xD5C68F465B2F9F81, 0xFCFF9CD288FDBAC5, - 0x77059157F359DC47, 0x1D262E3907FF492B, 0xFB582233E59AC557, 0xDDB2BCE242F8B673, - 0x2577B76248E096CF, 0x6F99C4A6D83DA74C, 0xC1147E41EB795701, 0xF48BAF76912A9337 - }; - static const uint64_t T4[256] = - { - 0x3EF29D249B2C0A19, 0xE9E16322B6F8622F, 0x5536994047757F7A, 0x9F4D56D5A47B0B33, - 0x822567466AA1174C, 0xB8F5057DEB082FB2, 0xCC48C10BF4475F53, 0x373088D4275DEC3A, - 0x968F4325180AED10, 0x173D232CF7016151, 0xAE4ED09F946FCC13, 0xFD4B4741C4539873, - 0x1B5B3F0DD9933765, 0x2FFCB0967B644052, 0xE02376D20A89840C, 0xA3AE3A70329B18D7, - 0x419CBD2335DE8526, 0xFAFEBF115B7C3199, 0x0397074F85AA9B0D, 0xC58AD4FB4836B970, - 0xBEC60BE3FC4104A8, 0x1EFF36DC4B708772, 0x131FDC33ED8453B6, 0x0844E33E341764D3, - 0x0FF11B6EAB38CD39, 0x64351F0A7761B85A, 0x3B5694F509CFBA0E, 0x30857084B87245D0, - 0x47AFB3BD2297AE3C, 0xF2BA5C2F6F6B554A, 0x74BDC4761F4F70E1, 0xCFDFC64471EDC45E, - 0xE610784C1DC0AF16, 0x7ACA29D63C113F28, 0x2DED411776A859AF, 0xAC5F211E99A3D5EE, - 0xD484F949A87EF33B, 0x3CE36CA596E013E4, 0xD120F0983A9D432C, 0x6BC40464DC597563, - 0x69D5F5E5D1956C9E, 0x9AE95F043698BB24, 0xC9ECC8DA66A4EF44, 0xD69508C8A5B2EAC6, - 0xC40C2235C0503B80, 0x38C193BA8C652103, 0x1CEEC75D46BC9E8F, 0xD331011937515AD1, - 0xD8E2E56886ECA50F, 0xB137108D5779C991, 0x709F3B6905CA4206, 0x4FEB50831680CAEF, - 0xEC456AF3241BD238, 0x58D673AFE181ABBE, 0x242F54E7CAD9BF8C, 0x0211F1810DCC19FD, - 0x90BC4DBB0F43C60A, 0x9518446A9DA0761D, 0xA1BFCBF13F57012A, 0x2BDE4F8961E172B5, - 0x27B853A84F732481, 0xB0B1E643DF1F4B61, 0x18CC38425C39AC68, 0xD2B7F7D7BF37D821, - 0x3103864A3014C720, 0x14AA246372ABFA5C, 0x6E600DB54EBAC574, 0x394765740403A3F3, - 0x09C215F0BC71E623, 0x2A58B947E987F045, 0x7B4CDF18B477BDD8, 0x9709B5EB906C6FE0, - 0x73083C268060D90B, 0xFEDC400E41F9037E, 0x284948C6E44BE9B8, 0x728ECAE808065BFB, - 0x06330E9E17492B1A, 0x5950856169E7294E, 0xBAE4F4FCE6C4364F, 0xCA7BCF95E30E7449, - 0x7D7FD186A33E96C2, 0x52836110D85AD690, 0x4DFAA1021B4CD312, 0x913ABB75872544FA, - 0xDD46ECB9140F1518, 0x3D659A6B1E869114, 0xC23F2CABD719109A, 0xD713FE062DD46836, - 0xD0A60656B2FBC1DC, 0x221C5A79DD909496, 0xEFD26DBCA1B14935, 0x0E77EDA0235E4FC9, - 0xCBFD395B6B68F6B9, 0x0DE0EAEFA6F4D4C4, 0x0422FF1F1A8532E7, 0xF969B85EDED6AA94, - 0x7F6E2007AEF28F3F, 0x3AD0623B81A938FE, 0x6624EE8B7AADA1A7, 0xB682E8DDC856607B, - 0xA78CC56F281E2A30, 0xC79B257A45FAA08D, 0x5B4174E0642B30B3, 0x5F638BFF7EAE0254, - 0x4BC9AF9C0C05F808, 0xCE59308AF98B46AE, 0x8FC58DA9CC55C388, 0x803496C7676D0EB1, - 0xF33CAAE1E70DD7BA, 0xBB6202326EA2B4BF, 0xD5020F87201871CB, 0x9D5CA754A9B712CE, - 0x841669D87DE83C56, 0x8A6184785EB6739F, 0x420BBA6CB0741E2B, 0xF12D5B60EAC1CE47, - 0x76AC35F71283691C, 0x2C6BB7D9FECEDB5F, 0xFCCDB18F4C351A83, 0x1F79C012C3160582, - 0xF0ABADAE62A74CB7, 0xE1A5801C82EF06FC, 0x67A21845F2CB2357, 0x5114665F5DF04D9D, - 0xBF40FD2D74278658, 0xA0393D3FB73183DA, 0x05A409D192E3B017, 0xA9FB28CF0B4065F9, - 0x25A9A22942BF3D7C, 0xDB75E22703463E02, 0xB326E10C5AB5D06C, 0xE7968E8295A62DE6, - 0xB973F3B3636EAD42, 0xDF571D3819C30CE5, 0xEE549B7229D7CBC5, 0x12992AFD65E2D146, - 0xF8EF4E9056B02864, 0xB7041E134030E28B, 0xC02EDD2ADAD50967, 0x932B4AF48AE95D07, - 0x6FE6FB7BC6DC4784, 0x239AACB755F61666, 0x401A4BEDBDB807D6, 0x485EA8D389AF6305, - 0xA41BC220ADB4B13D, 0x753B32B89729F211, 0x997E584BB3322029, 0x1D683193CEDA1C7F, - 0xFF5AB6C0C99F818E, 0x16BBD5E27F67E3A1, 0xA59D34EE25D233CD, 0x98F8AE853B54A2D9, - 0x6DF70AFACB105E79, 0x795D2E99B9BBA425, 0x8E437B6744334178, 0x0186F6CE886682F0, - 0xEBF092A3BB347BD2, 0xBCD7FA62F18D1D55, 0xADD9D7D011C5571E, 0x0BD3E471B1BDFFDE, - 0xAA6C2F808EEAFEF4, 0x5EE57D31F6C880A4, 0xF50FA47FF044FCA0, 0x1ADDC9C351F5B595, - 0xEA76646D3352F922, 0x0000000000000000, 0x85909F16F58EBEA6, 0x46294573AAF12CCC, - 0x0A5512BF39DB7D2E, 0x78DBD85731DD26D5, 0x29CFBE086C2D6B48, 0x218B5D36583A0F9B, - 0x152CD2ADFACD78AC, 0x83A39188E2C795BC, 0xC3B9DA655F7F926A, 0x9ECBA01B2C1D89C3, - 0x07B5F8509F2FA9EA, 0x7EE8D6C926940DCF, 0x36B67E1AAF3B6ECA, 0x86079859702425AB, - 0xFB7849DFD31AB369, 0x4C7C57CC932A51E2, 0xD96413A60E8A27FF, 0x263EA566C715A671, - 0x6C71FC344376DC89, 0x4A4F595284637AF8, 0xDAF314E98B20BCF2, 0x572768C14AB96687, - 0x1088DB7C682EC8BB, 0x887075F9537A6A62, 0x2E7A4658F302C2A2, 0x619116DBE582084D, - 0xA87DDE018326E709, 0xDCC01A779C6997E8, 0xEDC39C3DAC7D50C8, 0xA60A33A1A078A8C0, - 0xC1A82BE452B38B97, 0x3F746BEA134A88E9, 0xA228CCBEBAFD9A27, 0xABEAD94E068C7C04, - 0xF48952B178227E50, 0x5CF48CB0FB049959, 0x6017E0156DE48ABD, 0x4438B4F2A73D3531, - 0x8C528AE649FF5885, 0xB515EF924DFCFB76, 0x0C661C212E925634, 0xB493195CC59A7986, - 0x9CDA519A21D1903E, 0x32948105B5BE5C2D, 0x194ACE8CD45F2E98, 0x438D4CA238129CDB, - 0x9B6FA9CABEFE39D4, 0x81B26009EF0B8C41, 0xDED1EBF691A58E15, 0x4E6DA64D9EE6481F, - 0x54B06F8ECF13FD8A, 0x49D85E1D01C9E1F5, 0xAFC826511C094EE3, 0xF698A33075EE67AD, - 0x5AC7822EEC4DB243, 0x8DD47C28C199DA75, 0x89F68337DB1CE892, 0xCDCE37C57C21DDA3, - 0x530597DE503C5460, 0x6A42F2AA543FF793, 0x5D727A7E73621BA9, 0xE232875307459DF1, - 0x56A19E0FC2DFE477, 0xC61DD3B4CD9C227D, 0xE5877F03986A341B, 0x949EB2A415C6F4ED, - 0x6206119460289340, 0x6380E75AE84E11B0, 0x8BE772B6D6D0F16F, 0x50929091D596CF6D, - 0xE86795EC3E9EE0DF, 0x7CF927482B581432, 0xC86A3E14EEC26DB4, 0x7119CDA78DACC0F6, - 0xE40189CD100CB6EB, 0x92ADBC3A028FDFF7, 0xB2A017C2D2D3529C, 0x200DABF8D05C8D6B, - 0x34A78F9BA2F77737, 0xE3B4719D8F231F01, 0x45BE423C2F5BB7C1, 0xF71E55FEFD88E55D, - 0x6853032B59F3EE6E, 0x65B3E9C4FF073AAA, 0x772AC3399AE5EBEC, 0x87816E97F842A75B, - 0x110E2DB2E0484A4B, 0x331277CB3DD8DEDD, 0xBD510CAC79EB9FA5, 0x352179552A91F5C7 - }; - static const uint64_t T5[256] = - { - 0x8AB0A96846E06A6D, 0x43C7E80B4BF0B33A, 0x08C9B3546B161EE5, 0x39F1C235EBA990BE, - 0xC1BEF2376606C7B2, 0x2C209233614569AA, 0xEB01523B6FC3289A, 0x946953AB935ACEDD, - 0x272838F63E13340E, 0x8B0455ECA12BA052, 0x77A1B2C4978FF8A2, 0xA55122CA13E54086, - 0x2276135862D3F1CD, 0xDB8DDFDE08B76CFE, 0x5D1E12C89E4A178A, 0x0E56816B03969867, - 0xEE5F79953303ED59, 0xAFED748BAB78D71D, 0x6D929F2DF93E53EE, 0xF5D8A8F8BA798C2A, - 0xF619B1698E39CF6B, 0x95DDAF2F749104E2, 0xEC2A9C80E0886427, 0xCE5C8FD8825B95EA, - 0xC4E0D9993AC60271, 0x4699C3A5173076F9, 0x3D1B151F50A29F42, 0x9ED505EA2BC75946, - 0x34665ACFDC7F4B98, 0x61B1FB53292342F7, 0xC721C0080E864130, 0x8693CD1696FD7B74, - 0x872731927136B14B, 0xD3446C8A63A1721B, 0x669A35E8A6680E4A, 0xCAB658F239509A16, - 0xA4E5DE4EF42E8AB9, 0x37A7435EE83F08D9, 0x134E6239E26C7F96, 0x82791A3C2DF67488, - 0x3F6EF00A8329163C, 0x8E5A7E42FDEB6591, 0x5CAAEE4C7981DDB5, 0x19F234785AF1E80D, - 0x255DDDE3ED98BD70, 0x50898A32A99CCCAC, 0x28CA4519DA4E6656, 0xAE59880F4CB31D22, - 0x0D9798FA37D6DB26, 0x32F968F0B4FFCD1A, 0xA00F09644F258545, 0xFA3AD5175E24DE72, - 0xF46C547C5DB24615, 0x713E80FBFF0F7E20, 0x7843CF2B73D2AAFA, 0xBD17EA36AEDF62B4, - 0xFD111BACD16F92CF, 0x4ABAA7DBC72D67E0, 0xB3416B5DAD49FAD3, 0xBCA316B24914A88B, - 0x15D150068AECF914, 0xE27C1DEBE31EFC40, 0x4FE48C759BEDA223, 0x7EDCFD141B522C78, - 0x4E5070F17C26681C, 0xE696CAC15815F3BC, 0x35D2A64B3BB481A7, 0x800CFF29FE7DFDF6, - 0x1ED9FAC3D5BAA4B0, 0x6C2663A91EF599D1, 0x03C1199134404341, 0xF7AD4DED69F20554, - 0xCD9D9649B61BD6AB, 0xC8C3BDE7EADB1368, 0xD131899FB02AFB65, 0x1D18E352E1FAE7F1, - 0xDA39235AEF7CA6C1, 0xA1BBF5E0A8EE4F7A, 0x91377805CF9A0B1E, 0x3138716180BF8E5B, - 0xD9F83ACBDB3CE580, 0x0275E515D38B897E, 0x472D3F21F0FBBCC6, 0x2D946EB7868EA395, - 0xBA3C248D21942E09, 0xE7223645BFDE3983, 0xFF64FEB902E41BB1, 0xC97741630D10D957, - 0xC3CB1722B58D4ECC, 0xA27AEC719CAE0C3B, 0x99FECB51A48C15FB, 0x1465AC826D27332B, - 0xE1BD047AD75EBF01, 0x79F733AF941960C5, 0x672EC96C41A3C475, 0xC27FEBA6524684F3, - 0x64EFD0FD75E38734, 0xED9E60040743AE18, 0xFB8E2993B9EF144D, 0x38453EB10C625A81, - 0x6978480742355C12, 0x48CF42CE14A6EE9E, 0x1CAC1FD606312DCE, 0x7B82D6BA4792E9BB, - 0x9D141C7B1F871A07, 0x5616B80DC11C4A2E, 0xB849C198F21FA777, 0x7CA91801C8D9A506, - 0xB1348E487EC273AD, 0x41B20D1E987B3A44, 0x7460AB55A3CFBBE3, 0x84E628034576F20A, - 0x1B87D16D897A6173, 0x0FE27DEFE45D5258, 0x83CDE6B8CA3DBEB7, 0x0C23647ED01D1119, - 0x7A362A3EA0592384, 0xB61F40F3F1893F10, 0x75D457D1440471DC, 0x4558DA34237035B8, - 0xDCA6116587FC2043, 0x8D9B67D3C9AB26D0, 0x2B0B5C88EE0E2517, 0x6FE77A382AB5DA90, - 0x269CC472D9D8FE31, 0x63C41E46FAA8CB89, 0xB7ABBC771642F52F, 0x7D1DE4852F126F39, - 0xA8C6BA3024339BA0, 0x600507D7CEE888C8, 0x8FEE82C61A20AFAE, 0x57A2448926D78011, - 0xFCA5E72836A458F0, 0x072BCEBB8F4B4CBD, 0x497BBE4AF36D24A1, 0x3CAFE99BB769557D, - 0x12FA9EBD05A7B5A9, 0xE8C04BAA5B836BDB, 0x4273148FAC3B7905, 0x908384812851C121, - 0xE557D3506C55B0FD, 0x72FF996ACB4F3D61, 0x3EDA0C8E64E2DC03, 0xF0868356E6B949E9, - 0x04EAD72ABB0B0FFC, 0x17A4B5135967706A, 0xE3C8E16F04D5367F, 0xF84F30028DAF570C, - 0x1846C8FCBD3A2232, 0x5B8120F7F6CA9108, 0xD46FA231ECEA3EA6, 0x334D947453340725, - 0x58403966C28AD249, 0xBED6F3A79A9F21F5, 0x68CCB483A5FE962D, 0xD085751B57E1315A, - 0xFED0023DE52FD18E, 0x4B0E5B5F20E6ADDF, 0x1A332DE96EB1AB4C, 0xA3CE10F57B65C604, - 0x108F7BA8D62C3CD7, 0xAB07A3A11073D8E1, 0x6B0DAD1291BED56C, 0xF2F366433532C097, - 0x2E557726B2CEE0D4, 0x0000000000000000, 0xCB02A476DE9B5029, 0xE4E32FD48B9E7AC2, - 0x734B65EE2C84F75E, 0x6E5386BCCD7E10AF, 0x01B4FC84E7CBCA3F, 0xCFE8735C65905FD5, - 0x3613BFDA0FF4C2E6, 0x113B872C31E7F6E8, 0x2FE18BA255052AEB, 0xE974B72EBC48A1E4, - 0x0ABC5641B89D979B, 0xB46AA5E62202B66E, 0x44EC26B0C4BBFF87, 0xA6903B5B27A503C7, - 0x7F680190FC99E647, 0x97A84A3AA71A8D9C, 0xDD12EDE16037EA7C, 0xC554251DDD0DC84E, - 0x88C54C7D956BE313, 0x4D91696048662B5D, 0xB08072CC9909B992, 0xB5DE5962C5C97C51, - 0x81B803AD19B637C9, 0xB2F597D94A8230EC, 0x0B08AAC55F565DA4, 0xF1327FD2017283D6, - 0xAD98919E78F35E63, 0x6AB9519676751F53, 0x24E921670A53774F, 0xB9FD3D1C15D46D48, - 0x92F66194FBDA485F, 0x5A35DC7311015B37, 0xDED3F4705477A93D, 0xC00A0EB381CD0D8D, - 0xBB88D809C65FE436, 0x16104997BEACBA55, 0x21B70AC95693B28C, 0x59F4C5E225411876, - 0xD5DB5EB50B21F499, 0x55D7A19CF55C096F, 0xA97246B4C3F8519F, 0x8552D487A2BD3835, - 0x54635D181297C350, 0x23C2EFDC85183BF2, 0x9F61F96ECC0C9379, 0x534893A39DDC8FED, - 0x5EDF0B59AA0A54CB, 0xAC2C6D1A9F38945C, 0xD7AEBBA0D8AA7DE7, 0x2ABFA00C09C5EF28, - 0xD84CC64F3CF72FBF, 0x2003F64DB15878B3, 0xA724C7DFC06EC9F8, 0x069F323F68808682, - 0xCC296ACD51D01C94, 0x055E2BAE5CC0C5C3, 0x6270E2C21D6301B6, 0x3B842720382219C0, - 0xD2F0900E846AB824, 0x52FC6F277A1745D2, 0xC6953C8CE94D8B0F, 0xE009F8FE3095753E, - 0x655B2C7992284D0B, 0x984A37D54347DFC4, 0xEAB5AEBF8808E2A5, 0x9A3FD2C090CC56BA, - 0x9CA0E0FFF84CD038, 0x4C2595E4AFADE162, 0xDF6708F4B3BC6302, 0xBF620F237D54EBCA, - 0x93429D101C118260, 0x097D4FD08CDDD4DA, 0x8C2F9B572E60ECEF, 0x708A7C7F18C4B41F, - 0x3A30DBA4DFE9D3FF, 0x4006F19A7FB0F07B, 0x5F6BF7DD4DC19EF4, 0x1F6D064732716E8F, - 0xF9FBCC866A649D33, 0x308C8DE567744464, 0x8971B0F972A0292C, 0xD61A47243F61B7D8, - 0xEFEB8511D4C82766, 0x961CB6BE40D147A3, 0xAAB35F25F7B812DE, 0x76154E407044329D, - 0x513D76B64E570693, 0xF3479AC7D2F90AA8, 0x9B8B2E4477079C85, 0x297EB99D3D85AC69 - }; - static const uint64_t T6[256] = - { - 0x7E37E62DFC7D40C3, 0x776F25A4EE939E5B, 0xE045C850DD8FB5AD, 0x86ED5BA711FF1952, - 0xE91D0BD9CF616B35, 0x37E0AB256E408FFB, 0x9607F6C031025A7A, 0x0B02F5E116D23C9D, - 0xF3D8486BFB50650C, 0x621CFF27C40875F5, 0x7D40CB71FA5FD34A, 0x6DAA6616DAA29062, - 0x9F5F354923EC84E2, 0xEC847C3DC507C3B3, 0x025A3668043CE205, 0xA8BF9E6C4DAC0B19, - 0xFA808BE2E9BEBB94, 0xB5B99C5277C74FA3, 0x78D9BC95F0397BCC, 0xE332E50CDBAD2624, - 0xC74FCE129332797E, 0x1729ECEB2EA709AB, 0xC2D6B9F69954D1F8, 0x5D898CBFBAB8551A, - 0x859A76FB17DD8ADB, 0x1BE85886362F7FB5, 0xF6413F8FF136CD8A, 0xD3110FA5BBB7E35C, - 0x0A2FEED514CC4D11, 0xE83010EDCD7F1AB9, 0xA1E75DE55F42D581, 0xEEDE4A55C13B21B6, - 0xF2F5535FF94E1480, 0x0CC1B46D1888761E, 0xBCE15FDB6529913B, 0x2D25E8975A7181C2, - 0x71817F1CE2D7A554, 0x2E52C5CB5C53124B, 0xF9F7A6BEEF9C281D, 0x9E722E7D21F2F56E, - 0xCE170D9B81DCA7E6, 0x0E9B82051CB4941B, 0x1E712F623C49D733, 0x21E45CFA42F9F7DC, - 0xCB8E7A7F8BBA0F60, 0x8E98831A010FB646, 0x474CCF0D8E895B23, 0xA99285584FB27A95, - 0x8CC2B57205335443, 0x42D5B8E984EFF3A5, 0x012D1B34021E718C, 0x57A6626AAE74180B, - 0xFF19FC06E3D81312, 0x35BA9D4D6A7C6DFE, 0xC9D44C178F86ED65, 0x506523E6A02E5288, - 0x03772D5C06229389, 0x8B01F4FE0B691EC0, 0xF8DABD8AED825991, 0x4C4E3AEC985B67BE, - 0xB10DF0827FBF96A9, 0x6A69279AD4F8DAE1, 0xE78689DCD3D5FF2E, 0x812E1A2B1FA553D1, - 0xFBAD90D6EBA0CA18, 0x1AC543B234310E39, 0x1604F7DF2CB97827, 0xA6241C6951189F02, - 0x753513CCEAAF7C5E, 0x64F2A59FC84C4EFA, 0x247D2B1E489F5F5A, 0xDB64D718AB474C48, - 0x79F4A7A1F2270A40, 0x1573DA832A9BEBAE, 0x3497867968621C72, 0x514838D2A2302304, - 0xF0AF6537FD72F685, 0x1D06023E3A6B44BA, 0x678588C3CE6EDD73, 0x66A893F7CC70ACFF, - 0xD4D24E29B5EDA9DF, 0x3856321470EA6A6C, 0x07C3418C0E5A4A83, 0x2BCBB22F5635BACD, - 0x04B46CD00878D90A, 0x06EE5AB80C443B0F, 0x3B211F4876C8F9E5, 0x0958C38912EEDE98, - 0xD14B39CDBF8B0159, 0x397B292072F41BE0, 0x87C0409313E168DE, 0xAD26E98847CAA39F, - 0x4E140C849C6785BB, 0xD5FF551DB7F3D853, 0xA0CA46D15D5CA40D, 0xCD6020C787FE346F, - 0x84B76DCF15C3FB57, 0xDEFDA0FCA121E4CE, 0x4B8D7B6096012D3D, 0x9AC642AD298A2C64, - 0x0875D8BD10F0AF14, 0xB357C6EA7B8374AC, 0x4D6321D89A451632, 0xEDA96709C719B23F, - 0xF76C24BBF328BC06, 0xC662D526912C08F2, 0x3CE25EC47892B366, 0xB978283F6F4F39BD, - 0xC08C8F9E9D6833FD, 0x4F3917B09E79F437, 0x593DE06FB2C08C10, 0xD6887841B1D14BDA, - 0x19B26EEE32139DB0, 0xB494876675D93E2F, 0x825937771987C058, 0x90E9AC783D466175, - 0xF1827E03FF6C8709, 0x945DC0A8353EB87F, 0x4516F9658AB5B926, 0x3F9573987EB020EF, - 0xB855330B6D514831, 0x2AE6A91B542BCB41, 0x6331E413C6160479, 0x408F8E8180D311A0, - 0xEFF35161C325503A, 0xD06622F9BD9570D5, 0x8876D9A20D4B8D49, 0xA5533135573A0C8B, - 0xE168D364DF91C421, 0xF41B09E7F50A2F8F, 0x12B09B0F24C1A12D, 0xDA49CC2CA9593DC4, - 0x1F5C34563E57A6BF, 0x54D14F36A8568B82, 0xAF7CDFE043F6419A, 0xEA6A2685C943F8BC, - 0xE5DCBFB4D7E91D2B, 0xB27ADDDE799D0520, 0x6B443CAED6E6AB6D, 0x7BAE91C9F61BE845, - 0x3EB868AC7CAE5163, 0x11C7B65322E332A4, 0xD23C1491B9A992D0, 0x8FB5982E0311C7CA, - 0x70AC6428E0C9D4D8, 0x895BC2960F55FCC5, 0x76423E90EC8DEFD7, 0x6FF0507EDE9E7267, - 0x3DCF45F07A8CC2EA, 0x4AA06054941F5CB1, 0x5810FB5BB0DEFD9C, 0x5EFEA1E3BC9AC693, - 0x6EDD4B4ADC8003EB, 0x741808F8E8B10DD2, 0x145EC1B728859A22, 0x28BC9F7350172944, - 0x270A06424EBDCCD3, 0x972AEDF4331C2BF6, 0x059977E40A66A886, 0x2550302A4A812ED6, - 0xDD8A8DA0A7037747, 0xC515F87A970E9B7B, 0x3023EAA9601AC578, 0xB7E3AA3A73FBADA6, - 0x0FB699311EAAE597, 0x0000000000000000, 0x310EF19D6204B4F4, 0x229371A644DB6455, - 0x0DECAF591A960792, 0x5CA4978BB8A62496, 0x1C2B190A38753536, 0x41A295B582CD602C, - 0x3279DCC16426277D, 0xC1A194AA9F764271, 0x139D803B26DFD0A1, 0xAE51C4D441E83016, - 0xD813FA44AD65DFC1, 0xAC0BF2BC45D4D213, 0x23BE6A9246C515D9, 0x49D74D08923DCF38, - 0x9D05032127D066E7, 0x2F7FDEFF5E4D63C7, 0xA47E2A0155247D07, 0x99B16FF12FA8BFED, - 0x4661D4398C972AAF, 0xDFD0BBC8A33F9542, 0xDCA79694A51D06CB, 0xB020EBB67DA1E725, - 0xBA0F0563696DAA34, 0xE4F1A480D5F76CA7, 0xC438E34E9510EAF7, 0x939E81243B64F2FC, - 0x8DEFAE46072D25CF, 0x2C08F3A3586FF04E, 0xD7A56375B3CF3A56, 0x20C947CE40E78650, - 0x43F8A3DD86F18229, 0x568B795EAC6A6987, 0x8003011F1DBB225D, 0xF53612D3F7145E03, - 0x189F75DA300DEC3C, 0x9570DB9C3720C9F3, 0xBB221E576B73DBB8, 0x72F65240E4F536DD, - 0x443BE25188ABC8AA, 0xE21FFE38D9B357A8, 0xFD43CA6EE7E4F117, 0xCAA3614B89A47EEC, - 0xFE34E732E1C6629E, 0x83742C431B99B1D4, 0xCF3A16AF83C2D66A, 0xAAE5A8044990E91C, - 0x26271D764CA3BD5F, 0x91C4B74C3F5810F9, 0x7C6DD045F841A2C6, 0x7F1AFD19FE63314F, - 0xC8F957238D989CE9, 0xA709075D5306EE8E, 0x55FC5402AA48FA0E, 0x48FA563C9023BEB4, - 0x65DFBEABCA523F76, 0x6C877D22D8BCE1EE, 0xCC4D3BF385E045E3, 0xBEBB69B36115733E, - 0x10EAAD6720FD4328, 0xB6CEB10E71E5DC2A, 0xBDCC44EF6737E0B7, 0x523F158EA412B08D, - 0x989C74C52DB6CE61, 0x9BEB59992B945DE8, 0x8A2CEFCA09776F4C, 0xA3BD6B8D5B7E3784, - 0xEB473DB1CB5D8930, 0xC3FBA2C29B4AA074, 0x9C28181525CE176B, 0x683311F2D0C438E4, - 0x5FD3BAD7BE84B71F, 0xFC6ED15AE5FA809B, 0x36CDB0116C5EFE77, 0x29918447520958C8, - 0xA29070B959604608, 0x53120EBAA60CC101, 0x3A0C047C74D68869, 0x691E0AC6D2DA4968, - 0x73DB4974E6EB4751, 0x7A838AFDF40599C9, 0x5A4ACD33B4E21F99, 0x6046C94FC03497F0, - 0xE6AB92E8D1CB8EA2, 0x3354C7F5663856F1, 0xD93EE170AF7BAE4D, 0x616BD27BC22AE67C, - 0x92B39A10397A8370, 0xABC8B3304B8E9890, 0xBF967287630B02B2, 0x5B67D607B6FC6E15 - }; - static uint64_t T7[256] = - { - 0xD031C397CE553FE6, 0x16BA5B01B006B525, 0xA89BADE6296E70C8, 0x6A1F525D77D3435B, - 0x6E103570573DFA0B, 0x660EFB2A17FC95AB, 0x76327A9E97634BF6, 0x4BAD9D6462458BF5, - 0xF1830CAEDBC3F748, 0xC5C8F542669131FF, 0x95044A1CDC48B0CB, 0x892962DF3CF8B866, - 0xB0B9E208E930C135, 0xA14FB3F0611A767C, 0x8D2605F21C160136, 0xD6B71922FECC549E, - 0x37089438A5907D8B, 0x0B5DA38E5803D49C, 0x5A5BCC9CEA6F3CBC, 0xEDAE246D3B73FFE5, - 0xD2B87E0FDE22EDCE, 0x5E54ABB1CA8185EC, 0x1DE7F88FE80561B9, 0xAD5E1A870135A08C, - 0x2F2ADBD665CECC76, 0x5780B5A782F58358, 0x3EDC8A2EEDE47B3F, 0xC9D95C3506BEE70F, - 0x83BE111D6C4E05EE, 0xA603B90959367410, 0x103C81B4809FDE5D, 0x2C69B6027D0C774A, - 0x399080D7D5C87953, 0x09D41E16487406B4, 0xCDD63B1826505E5F, 0xF99DC2F49B0298E8, - 0x9CD0540A943CB67F, 0xBCA84B7F891F17C5, 0x723D1DB3B78DF2A6, 0x78AA6E71E73B4F2E, - 0x1433E699A071670D, 0x84F21BE454620782, 0x98DF3327B4D20F2F, 0xF049DCE2D3769E5C, - 0xDB6C60199656EB7A, 0x648746B2078B4783, 0x32CD23598DCBADCF, 0x1EA4955BF0C7DA85, - 0xE9A143401B9D46B5, 0xFD92A5D9BBEC21B8, 0xC8138C790E0B8E1B, 0x2EE00B9A6D7BA562, - 0xF85712B893B7F1FC, 0xEB28FED80BEA949D, 0x564A65EB8A40EA4C, 0x6C9988E8474A2823, - 0x4535898B121D8F2D, 0xABD8C03231ACCBF4, 0xBA2E91CAB9867CBD, 0x7960BE3DEF8E263A, - 0x0C11A977602FD6F0, 0xCB50E1AD16C93527, 0xEAE22E94035FFD89, 0x2866D12F5DE2CE1A, - 0xFF1B1841AB9BF390, 0x9F9339DE8CFE0D43, 0x964727C8C48A0BF7, 0x524502C6AAAE531C, - 0x9B9C5EF3AC10B413, 0x4FA2FA4942AB32A5, 0x3F165A62E551122B, 0xC74148DA76E6E3D7, - 0x924840E5E464B2A7, 0xD372AE43D69784DA, 0x233B72A105E11A86, 0xA48A04914941A638, - 0xB4B68525C9DE7865, 0xDDEABAACA6CF8002, 0x0A9773C250B6BD88, 0xC284FFBB5EBD3393, - 0x8BA0DF472C8F6A4E, 0x2AEF6CB74D951C32, 0x427983722A318D41, 0x73F7CDFFBF389BB2, - 0x074C0AF9382C026C, 0x8A6A0F0B243A035A, 0x6FDAE53C5F88931F, 0xC68B98967E538AC3, - 0x44FF59C71AA8E639, 0xE2FCE0CE439E9229, 0xA20CDE2479D8CD40, 0x19E89FA2C8EBD8E9, - 0xF446BBCFF398270C, 0x43B3533E2284E455, 0xD82F0DCD8E945046, 0x51066F12B26CE820, - 0xE73957AF6BC5426D, 0x081ECE5A40C16FA0, 0x3B193D4FC5BFAB7B, 0x7FE66488DF174D42, - 0x0E9814EF705804D8, 0x8137AC857C39D7C6, 0xB1733244E185A821, 0x695C3F896F11F867, - 0xF6CF0657E3EFF524, 0x1AABF276D02963D5, 0x2DA3664E75B91E5E, 0x0289BD981077D228, - 0x90C1FD7DF413608F, 0x3C5537B6FD93A917, 0xAA12107E3919A2E0, 0x0686DAB530996B78, - 0xDAA6B0559EE3826E, 0xC34E2FF756085A87, 0x6D5358A44FFF4137, 0xFC587595B35948AC, - 0x7CA5095CC7D5F67E, 0xFB147F6C8B754AC0, 0xBFEB26AB91DDACF9, 0x6896EFC567A49173, - 0xCA9A31E11E7C5C33, 0xBBE44186B13315A9, 0x0DDB793B689ABFE4, 0x70B4A02BA7FA208E, - 0xE47A3A7B7307F951, 0x8CECD5BE14A36822, 0xEEED49B923B144D9, 0x17708B4DB8B3DC31, - 0x6088219F2765FED3, 0xB3FA8FDCF1F27A09, 0x910B2D31FCA6099B, 0x0F52C4A378ED6DCC, - 0x50CCBF5EBAD98134, 0x6BD582117F662A4F, 0x94CE9A50D4FDD9DF, 0x2B25BCFB45207526, - 0x67C42B661F49FCBF, 0x492420FC723259DD, 0x03436DD418C2BB3C, 0x1F6E4517F872B391, - 0xA08563BC69AF1F68, 0xD43EA4BAEEBB86B6, 0x01CAD04C08B56914, 0xAC94CACB0980C998, - 0x54C3D8739A373864, 0x26FEC5C02DBACAC2, 0xDEA9D778BE0D3B3E, 0x040F672D20EEB950, - 0xE5B0EA377BB29045, 0xF30AB136CBB42560, 0x62019C0737122CFB, 0xE86B930C13282FA1, - 0xCC1CEB542EE5374B, 0x538FD28AA21B3A08, 0x1B61223AD89C0AC1, 0x36C24474AD25149F, - 0x7A23D3E9F74C9D06, 0xBE21F6E79968C5ED, 0xCF5F868036278C77, 0xF705D61BEB5A9C30, - 0x4D2B47D152DCE08D, 0x5F9E7BFDC234ECF8, 0x247778583DCD18EA, 0x867BA67C4415D5AA, - 0x4CE1979D5A698999, 0x0000000000000000, 0xEC64F42133C696F1, 0xB57C5569C16B1171, - 0xC1C7926F467F88AF, 0x654D96FE0F3E2E97, 0x15F936D5A8C40E19, 0xB8A72C52A9F1AE95, - 0xA9517DAA21DB19DC, 0x58D27104FA18EE94, 0x5918A148F2AD8780, 0x5CDD1629DAF657C4, - 0x8274C15164FB6CFA, 0xD1FB13DBC6E056F2, 0x7D6FD910CF609F6A, 0xB63F38BDD9A9AA4D, - 0x3D9FE7FAF526C003, 0x74BBC706871499DE, 0xDF630734B6B8522A, 0x3AD3ED03CD0AC26F, - 0xFADEAF2083C023D4, 0xC00D42234ECAE1BB, 0x8538CBA85CD76E96, 0xC402250E6E2458EB, - 0x47BC3413026A5D05, 0xAFD7A71F114272A4, 0x978DF784CC3F62E3, 0xB96DFC1EA144C781, - 0x21B2CF391596C8AE, 0x318E4E8D950916F3, 0xCE9556CC3E92E563, 0x385A509BDD7D1047, - 0x358129A0B5E7AFA3, 0xE6F387E363702B79, 0xE0755D5653E94001, 0x7BE903A5FFF9F412, - 0x12B53C2C90E80C75, 0x3307F315857EC4DB, 0x8FAFB86A0C61D31E, 0xD9E5DD8186213952, - 0x77F8AAD29FD622E2, 0x25BDA814357871FE, 0x7571174A8FA1F0CA, 0x137FEC60985D6561, - 0x30449EC19DBC7FE7, 0xA540D4DD41F4CF2C, 0xDC206AE0AE7AE916, 0x5B911CD0E2DA55A8, - 0xB2305F90F947131D, 0x344BF9ECBD52C6B7, 0x5D17C665D2433ED0, 0x18224FEEC05EB1FD, - 0x9E59E992844B6457, 0x9A568EBFA4A5DD07, 0xA3C60E68716DA454, 0x7E2CB4C4D7A22456, - 0x87B176304CA0BCBE, 0x413AEEA632F3367D, 0x9915E36BBC67663B, 0x40F03EEA3A465F69, - 0x1C2D28C3E0B008AD, 0x4E682A054A1E5BB1, 0x05C5B761285BD044, 0xE1BF8D1A5B5C2915, - 0xF2C0617AC3014C74, 0xB7F5E8F1D11CC359, 0x63CB4C4B3FA745EF, 0x9D1A84469C89DF6B, - 0xE33630824B2BFB3D, 0xD5F474F6E60EEFA2, 0xF58C6B83FB2D4E18, 0x4676E45F0ADF3411, - 0x20781F751D23A1BA, 0xBD629B3381AA7ED1, 0xAE1D775319F71BB0, 0xFED1C80DA32E9A84, - 0x5509083F92825170, 0x29AC01635557A70E, 0xA7C9694551831D04, 0x8E65682604D4BA0A, - 0x11F651F8882AB749, 0xD77DC96EF6793D8A, 0xEF2799F52B042DCD, 0x48EEF0B07A8730C9, - 0x22F1A2ED0D547392, 0x6142F1D32FD097C7, 0x4A674D286AF0E2E1, 0x80FD7CC9748CBED2, - 0x717E7067AF4F499A, 0x938290A9ECD1DBB3, 0x88E3B293344DD172, 0x2734158C250FA3D6 - }; + static const uint64_t T0[256] = + { + 0xE6F87E5C5B711FD0, 0x258377800924FA16, 0xC849E07E852EA4A8, 0x5B4686A18F06C16A, + 0x0B32E9A2D77B416E, 0xABDA37A467815C66, 0xF61796A81A686676, 0xF5DC0B706391954B, + 0x4862F38DB7E64BF1, 0xFF5C629A68BD85C5, 0xCB827DA6FCD75795, 0x66D36DAF69B9F089, + 0x356C9F74483D83B0, 0x7CBCECB1238C99A1, 0x36A702AC31C4708D, 0x9EB6A8D02FBCDFD6, + 0x8B19FA51E5B3AE37, 0x9CCFB5408A127D0B, 0xBC0C78B508208F5A, 0xE533E3842288ECED, + 0xCEC2C7D377C15FD2, 0xEC7817B6505D0F5E, 0xB94CC2C08336871D, 0x8C205DB4CB0B04AD, + 0x763C855B28A0892F, 0x588D1B79F6FF3257, 0x3FECF69E4311933E, 0x0FC0D39F803A18C9, + 0xEE010A26F5F3AD83, 0x10EFE8F4411979A6, 0x5DCDA10C7DE93A10, 0x4A1BEE1D1248E92C, + 0x53BFF2DB21847339, 0xB4F50CCFA6A23D09, 0x5FB4BC9CD84798CD, 0xE88A2D8B071C56F9, + 0x7F7771695A756A9C, 0xC5F02E71A0BA1EBC, 0xA663F9AB4215E672, 0x2EB19E22DE5FBB78, + 0x0DB9CE0F2594BA14, 0x82520E6397664D84, 0x2F031E6A0208EA98, 0x5C7F2144A1BE6BF0, + 0x7A37CB1CD16362DB, 0x83E08E2B4B311C64, 0xCF70479BAB960E32, 0x856BA986B9DEE71E, + 0xB5478C877AF56CE9, 0xB8FE42885F61D6FD, 0x1BDD0156966238C8, 0x622157923EF8A92E, + 0xFC97FF42114476F8, 0x9D7D350856452CEB, 0x4C90C9B0E0A71256, 0x2308502DFBCB016C, + 0x2D7A03FAA7A64845, 0xF46E8B38BFC6C4AB, 0xBDBEF8FDD477DEBA, 0x3AAC4CEBC8079B79, + 0xF09CB105E8879D0C, 0x27FA6A10AC8A58CB, 0x8960E7C1401D0CEA, 0x1A6F811E4A356928, + 0x90C4FB0773D196FF, 0x43501A2F609D0A9F, 0xF7A516E0C63F3796, 0x1CE4A6B3B8DA9252, + 0x1324752C38E08A9B, 0xA5A864733BEC154F, 0x2BF124575549B33F, 0xD766DB15440DC5C7, + 0xA7D179E39E42B792, 0xDADF151A61997FD3, 0x86A0345EC0271423, 0x38D5517B6DA939A4, + 0x6518F077104003B4, 0x02791D90A5AEA2DD, 0x88D267899C4A5D0A, 0x930F66DF0A2865C2, + 0x4EE9D4204509B08B, 0x325538916685292A, 0x412907BFC533A842, 0xB27E2B62544DC673, + 0x6C5304456295E007, 0x5AF406E95351908A, 0x1F2F3B6BC123616F, 0xC37B09DC5255E5C6, + 0x3967D133B1FE6844, 0x298839C7F0E711E2, 0x409B87F71964F9A2, 0xE938ADC3DB4B0719, + 0x0C0B4E47F9C3EBF4, 0x5534D576D36B8843, 0x4610A05AEB8B02D8, 0x20C3CDF58232F251, + 0x6DE1840DBEC2B1E7, 0xA0E8DE06B0FA1D08, 0x7B854B540D34333B, 0x42E29A67BCCA5B7F, + 0xD8A6088AC437DD0E, 0xC63BB3A9D943ED81, 0x21714DBD5E65A3B1, 0x6761EDE7B5EEA169, + 0x2431F7C8D573ABF6, 0xD51FC685E1A3671A, 0x5E063CD40410C92D, 0x283AB98F2CB04002, + 0x8FEBC06CB2F2F790, 0x17D64F116FA1D33C, 0xE07359F1A99EE4AA, 0x784ED68C74CDC006, + 0x6E2A19D5C73B42DA, 0x8712B4161C7045C3, 0x371582E4ED93216D, 0xACE390414939F6FC, + 0x7EC5F12186223B7C, 0xC0B094042BAC16FB, 0xF9D745379A527EBF, 0x737C3F2EA3B68168, + 0x33E7B8D9BAD278CA, 0xA9A32A34C22FFEBB, 0xE48163CCFEDFBD0D, 0x8E5940246EA5A670, + 0x51C6EF4B842AD1E4, 0x22BAD065279C508C, 0xD91488C218608CEE, 0x319EA5491F7CDA17, + 0xD394E128134C9C60, 0x094BF43272D5E3B3, 0x9BF612A5A4AAD791, 0xCCBBDA43D26FFD0F, + 0x34DE1F3C946AD250, 0x4F5B5468995EE16B, 0xDF9FAF6FEA8F7794, 0x2648EA5870DD092B, + 0xBFC7E56D71D97C67, 0xDDE6B2FF4F21D549, 0x3C276B463AE86003, 0x91767B4FAF86C71F, + 0x68A13E7835D4B9A0, 0xB68C115F030C9FD4, 0x141DD2C916582001, 0x983D8F7DDD5324AC, + 0x64AA703FCC175254, 0xC2C989948E02B426, 0x3E5E76D69F46C2DE, 0x50746F03587D8004, + 0x45DB3D829272F1E5, 0x60584A029B560BF3, 0xFBAE58A73FFCDC62, 0xA15A5E4E6CAD4CE8, + 0x4BA96E55CE1FB8CC, 0x08F9747AAE82B253, 0xC102144CF7FB471B, 0x9F042898F3EB8E36, + 0x068B27ADF2EFFB7A, 0xEDCA97FE8C0A5EBE, 0x778E0513F4F7D8CF, 0x302C2501C32B8BF7, + 0x8D92DDFC175C554D, 0xF865C57F46052F5F, 0xEAF3301BA2B2F424, 0xAA68B7ECBBD60D86, + 0x998F0F350104754C, 0x0000000000000000, 0xF12E314D34D0CCEC, 0x710522BE061823B5, + 0xAF280D9930C005C1, 0x97FD5CE25D693C65, 0x19A41CC633CC9A15, 0x95844172F8C79EB8, + 0xDC5432B7937684A9, 0x9436C13A2490CF58, 0x802B13F332C8EF59, 0xC442AE397CED4F5C, + 0xFA1CD8EFE3AB8D82, 0xF2E5AC954D293FD1, 0x6AD823E8907A1B7D, 0x4D2249F83CF043B6, + 0x03CB9DD879F9F33D, 0xDE2D2F2736D82674, 0x2A43A41F891EE2DF, 0x6F98999D1B6C133A, + 0xD4AD46CD3DF436FA, 0xBB35DF50269825C0, 0x964FDCAA813E6D85, 0xEB41B0537EE5A5C4, + 0x0540BA758B160847, 0xA41AE43BE7BB44AF, 0xE3B8C429D0671797, 0x819993BBEE9FBEB9, + 0xAE9A8DD1EC975421, 0xF3572CDD917E6E31, 0x6393D7DAE2AFF8CE, 0x47A2201237DC5338, + 0xA32343DEC903EE35, 0x79FC56C4A89A91E6, 0x01B28048DC5751E0, 0x1296F564E4B7DB7B, + 0x75F7188351597A12, 0xDB6D9552BDCE2E33, 0x1E9DBB231D74308F, 0x520D7293FDD322D9, + 0xE20A44610C304677, 0xFEEEE2D2B4EAD425, 0xCA30FDEE20800675, 0x61EACA4A47015A13, + 0xE74AFE1487264E30, 0x2CC883B27BF119A5, 0x1664CF59B3F682DC, 0xA811AA7C1E78AF5B, + 0x1D5626FB648DC3B2, 0xB73E9117DF5BCE34, 0xD05F7CF06AB56F5D, 0xFD257F0ACD132718, + 0x574DC8E676C52A9E, 0x0739A7E52EB8AA9A, 0x5486553E0F3CD9A3, 0x56FF48AEAA927B7E, + 0xBE756525AD8E2D87, 0x7D0E6CF9FFDBC841, 0x3B1ECCA31450CA99, 0x6913BE30E983E840, + 0xAD511009956EA71C, 0xB1B5B6BA2DB4354E, 0x4469BDCA4E25A005, 0x15AF5281CA0F71E1, + 0x744598CB8D0E2BF2, 0x593F9B312AA863B7, 0xEFB38A6E29A4FC63, 0x6B6AA3A04C2D4A9D, + 0x3D95EB0EE6BF31E3, 0xA291C3961554BFD5, 0x18169C8EEF9BCBF5, 0x115D68BC9D4E2846, + 0xBA875F18FACF7420, 0xD1EDFCB8B6E23EBD, 0xB00736F2F1E364AE, 0x84D929CE6589B6FE, + 0x70B7A2F6DA4F7255, 0x0E7253D75C6D4929, 0x04F23A3D574159A7, 0x0A8069EA0B2C108E, + 0x49D073C56BB11A11, 0x8AAB7A1939E4FFD7, 0xCD095A0B0E38ACEF, 0xC9FB60365979F548, + 0x92BDE697D67F3422, 0xC78933E10514BC61, 0xE1C1D9B975C9B54A, 0xD2266160CF1BCD80, + 0x9A4492ED78FD8671, 0xB3CCAB2A881A9793, 0x72CEBF667FE1D088, 0xD6D45B5D985A9427 + }; + static const uint64_t T1[256] = + { + 0xC811A8058C3F55DE, 0x65F5B43196B50619, 0xF74F96B1D6706E43, 0x859D1E8BCB43D336, + 0x5AAB8A85CCFA3D84, 0xF9C7BF99C295FCFD, 0xA21FD5A1DE4B630F, 0xCDB3EF763B8B456D, + 0x803F59F87CF7C385, 0xB27C73BE5F31913C, 0x98E3AC6633B04821, 0xBF61674C26B8F818, + 0x0FFBC995C4C130C8, 0xAAA0862010761A98, 0x6057F342210116AA, 0xF63C760C0654CC35, + 0x2DDB45CC667D9042, 0xBCF45A964BD40382, 0x68E8A0C3EF3C6F3D, 0xA7BD92D269FF73BC, + 0x290AE20201ED2287, 0xB7DE34CDE885818F, 0xD901EEA7DD61059B, 0xD6FA273219A03553, + 0xD56F1AE874CCCEC9, 0xEA31245C2E83F554, 0x7034555DA07BE499, 0xCE26D2AC56E7BEF7, + 0xFD161857A5054E38, 0x6A0E7DA4527436D1, 0x5BD86A381CDE9FF2, 0xCAF7756231770C32, + 0xB09AAED9E279C8D0, 0x5DEF1091C60674DB, 0x111046A2515E5045, 0x23536CE4729802FC, + 0xC50CBCF7F5B63CFA, 0x73A16887CD171F03, 0x7D2941AFD9F28DBD, 0x3F5E3EB45A4F3B9D, + 0x84EEFE361B677140, 0x3DB8E3D3E7076271, 0x1A3A28F9F20FD248, 0x7EBC7C75B49E7627, + 0x74E5F293C7EB565C, 0x18DCF59E4F478BA4, 0x0C6EF44FA9ADCB52, 0xC699812D98DAC760, + 0x788B06DC6E469D0E, 0xFC65F8EA7521EC4E, 0x30A5F7219E8E0B55, 0x2BEC3F65BCA57B6B, + 0xDDD04969BAF1B75E, 0x99904CDBE394EA57, 0x14B201D1E6EA40F6, 0xBBB0C08241284ADD, + 0x50F20463BF8F1DFF, 0xE8D7F93B93CBACB8, 0x4D8CB68E477C86E8, 0xC1DD1B3992268E3F, + 0x7C5AA11209D62FCB, 0x2F3D98ABDB35C9AE, 0x671369562BFD5FF5, 0x15C1E16C36CEE280, + 0x1D7EB2EDF8F39B17, 0xDA94D37DB00DFE01, 0x877BC3EC760B8ADA, 0xCB8495DFE153AE44, + 0x05A24773B7B410B3, 0x12857B783C32ABDF, 0x8EB770D06812513B, 0x536739B9D2E3E665, + 0x584D57E271B26468, 0xD789C78FC9849725, 0xA935BBFA7D1AE102, 0x8B1537A3DFA64188, + 0xD0CD5D9BC378DE7A, 0x4AC82C9A4D80CFB7, 0x42777F1B83BDB620, 0x72D2883A1D33BD75, + 0x5E7A2D4BAB6A8F41, 0xF4DAAB6BBB1C95D9, 0x905CFFE7FD8D31B6, 0x83AA6422119B381F, + 0xC0AEFB8442022C49, 0xA0F908C663033AE3, 0xA428AF0804938826, 0xADE41C341A8A53C7, + 0xAE7121EE77E6A85D, 0xC47F5C4A25929E8C, 0xB538E9AA55CDD863, 0x06377AA9DAD8EB29, + 0xA18AE87BB3279895, 0x6EDFDA6A35E48414, 0x6B7D9D19825094A7, 0xD41CFA55A4E86CBF, + 0xE5CAEDC9EA42C59C, 0xA36C351C0E6FC179, 0x5181E4DE6FABBF89, 0xFFF0C530184D17D4, + 0x9D41EB1584045892, 0x1C0D525028D73961, 0xF178EC180CA8856A, 0x9A0571018EF811CD, + 0x4091A27C3EF5EFCC, 0x19AF15239F6329D2, 0x347450EFF91EB990, 0xE11B4A078DD27759, + 0xB9561DE5FC601331, 0x912F1F5A2DA993C0, 0x1654DCB65BA2191A, 0x3E2DDE098A6B99EB, + 0x8A66D71E0F82E3FE, 0x8C51ADB7D55A08D7, 0x4533E50F8941FF7F, 0x02E6DD67BD4859EC, + 0xE068AABA5DF6D52F, 0xC24826E3FF4A75A5, 0x6C39070D88ACDDF8, 0x6486548C4691A46F, + 0xD1BEBD26135C7C0C, 0xB30F93038F15334A, 0x82D9849FC1BF9A69, 0x9C320BA85420FAE4, + 0xFA528243AFF90767, 0x9ED4D6CFE968A308, 0xB825FD582C44B147, 0x9B7691BC5EDCB3BB, + 0xC7EA619048FE6516, 0x1063A61F817AF233, 0x47D538683409A693, 0x63C2CE984C6DED30, + 0x2A9FDFD86C81D91D, 0x7B1E3B06032A6694, 0x666089EBFBD9FD83, 0x0A598EE67375207B, + 0x07449A140AFC495F, 0x2CA8A571B6593234, 0x1F986F8A45BBC2FB, 0x381AA4A050B372C2, + 0x5423A3ADD81FAF3A, 0x17273C0B8B86BB6C, 0xFE83258DC869B5A2, 0x287902BFD1C980F1, + 0xF5A94BD66B3837AF, 0x88800A79B2CABA12, 0x55504310083B0D4C, 0xDF36940E07B9EEB2, + 0x04D1A7CE6790B2C5, 0x612413FFF125B4DC, 0x26F12B97C52C124F, 0x86082351A62F28AC, + 0xEF93632F9937E5E7, 0x3507B052293A1BE6, 0xE72C30AE570A9C70, 0xD3586041AE1425E0, + 0xDE4574B3D79D4CC4, 0x92BA228040C5685A, 0xF00B0CA5DC8C271C, 0xBE1287F1F69C5A6E, + 0xF39E317FB1E0DC86, 0x495D114020EC342D, 0x699B407E3F18CD4B, 0xDCA3A9D46AD51528, + 0x0D1D14F279896924, 0x0000000000000000, 0x593EB75FA196C61E, 0x2E4E78160B116BD8, + 0x6D4AE7B058887F8E, 0xE65FD013872E3E06, 0x7A6DDBBBD30EC4E2, 0xAC97FC89CAAEF1B1, + 0x09CCB33C1E19DBE1, 0x89F3EAC462EE1864, 0x7770CF49AA87ADC6, 0x56C57ECA6557F6D6, + 0x03953DDA6D6CFB9A, 0x36928D884456E07C, 0x1EEB8F37959F608D, 0x31D6179C4EAAA923, + 0x6FAC3AD7E5C02662, 0x43049FA653991456, 0xABD3669DC052B8EE, 0xAF02C153A7C20A2B, + 0x3CCB036E3723C007, 0x93C9C23D90E1CA2C, 0xC33BC65E2F6ED7D3, 0x4CFF56339758249E, + 0xB1E94E64325D6AA6, 0x37E16D359472420A, 0x79F8E661BE623F78, 0x5214D90402C74413, + 0x482EF1FDF0C8965B, 0x13F69BC5EC1609A9, 0x0E88292814E592BE, 0x4E198B542A107D72, + 0xCCC00FCBEBAFE71B, 0x1B49C844222B703E, 0x2564164DA840E9D5, 0x20C6513E1FF4F966, + 0xBAC3203F910CE8AB, 0xF2EDD1C261C47EF0, 0x814CB945ACD361F3, 0x95FEB8944A392105, + 0x5C9CF02C1622D6AD, 0x971865F3F77178E9, 0xBD87BA2B9BF0A1F4, 0x444005B259655D09, + 0xED75BE48247FBC0B, 0x7596122E17CFF42A, 0xB44B091785E97A15, 0x966B854E2755DA9F, + 0xEEE0839249134791, 0x32432A4623C652B9, 0xA8465B47AD3E4374, 0xF8B45F2412B15E8B, + 0x2417F6F078644BA3, 0xFB2162FE7FDDA511, 0x4BBBCC279DA46DC1, 0x0173E0BDD024A276, + 0x22208C59A2BCA08A, 0x8FC4906DB836F34D, 0xE4B90D743A6667EA, 0x7147B5E0705F46EF, + 0x2782CB2A1508B039, 0xEC065EF5F45B1E7D, 0x21B5B183CFD05B10, 0xDBE733C060295C77, + 0x9FA73672394C017E, 0xCF55321186C31C81, 0xD8720E1A0D45A7ED, 0x3B8F997A3DDF8958, + 0x3AFC79C7EDFB2B2E, 0xE9A4198643EF0ECE, 0x5F09CDF67B4E2D37, 0x4F6A6BE9FA34DF04, + 0xB6ADD47038A123F9, 0x8D224D0A057EAAA1, 0xC96248B85C1BF7A8, 0xE3FD9760309A2EB5, + 0x0B2A6E5BA351820D, 0xEB42C4E1FEA75722, 0x948D58299A1D8373, 0x7FCF9CC864BAD451, + 0xA55B4FB5D4B72A50, 0x08BF5381CE3D7997, 0x46A6D8D5E42D04E5, 0xD22B80FC7E308796, + 0x57B69E77B57354A0, 0x3969441D8097D0B4, 0x3330CAFBF3E2F0CF, 0xE28E77DDE0BE8CC3, + 0x62B12E259C494F46, 0xA6CE726FB9DBD1CA, 0x41E242C1EED14DBA, 0x76032FF47AA30FB0 + }; + static const uint64_t T2[256] = + { + 0x45B268A93ACDE4CC, 0xAF7F0BE884549D08, 0x048354B3C1468263, 0x925435C2C80EFED2, + 0xEE4E37F27FDFFBA7, 0x167A33920C60F14D, 0xFB123B52EA03E584, 0x4A0CAB53FDBB9007, + 0x9DEAF6380F788A19, 0xCB48EC558F0CB32A, 0xB59DC4B2D6FEF7E0, 0xDCDBCA22F4F3ECB6, + 0x11DF5813549A9C40, 0xE33FDEDF568ACED3, 0xA0C1C8124322E9C3, 0x07A56B8158FA6D0D, + 0x77279579B1E1F3DD, 0xD9B18B74422AC004, 0xB8EC2D9FFFABC294, 0xF4ACF8A82D75914F, + 0x7BBF69B1EF2B6878, 0xC4F62FAF487AC7E1, 0x76CE809CC67E5D0C, 0x6711D88F92E4C14C, + 0x627B99D9243DEDFE, 0x234AA5C3DFB68B51, 0x909B1F15262DBF6D, 0x4F66EA054B62BCB5, + 0x1AE2CF5A52AA6AE8, 0xBEA053FBD0CE0148, 0xED6808C0E66314C9, 0x43FE16CD15A82710, + 0xCD049231A06970F6, 0xE7BC8A6C97CC4CB0, 0x337CE835FCB3B9C0, 0x65DEF2587CC780F3, + 0x52214EDE4132BB50, 0x95F15E4390F493DF, 0x870839625DD2E0F1, 0x41313C1AFB8B66AF, + 0x91720AF051B211BC, 0x477D427ED4EEA573, 0x2E3B4CEEF6E3BE25, 0x82627834EB0BCC43, + 0x9C03E3DD78E724C8, 0x2877328AD9867DF9, 0x14B51945E243B0F2, 0x574B0F88F7EB97E2, + 0x88B6FA989AA4943A, 0x19C4F068CB168586, 0x50EE6409AF11FAEF, 0x7DF317D5C04EABA4, + 0x7A567C5498B4C6A9, 0xB6BBFB804F42188E, 0x3CC22BCF3BC5CD0B, 0xD04336EAAA397713, + 0xF02FAC1BEC33132C, 0x2506DBA7F0D3488D, 0xD7E65D6BF2C31A1E, 0x5EB9B2161FF820F5, + 0x842E0650C46E0F9F, 0x716BEB1D9E843001, 0xA933758CAB315ED4, 0x3FE414FDA2792265, + 0x27C9F1701EF00932, 0x73A4C1CA70A771BE, 0x94184BA6E76B3D0E, 0x40D829FF8C14C87E, + 0x0FBEC3FAC77674CB, 0x3616A9634A6A9572, 0x8F139119C25EF937, 0xF545ED4D5AEA3F9E, + 0xE802499650BA387B, 0x6437E7BD0B582E22, 0xE6559F89E053E261, 0x80AD52E305288DFC, + 0x6DC55A23E34B9935, 0xDE14E0F51AD0AD09, 0xC6390578A659865E, 0x96D7617109487CB1, + 0xE2D6CB3A21156002, 0x01E915E5779FAED1, 0xADB0213F6A77DCB7, 0x9880B76EB9A1A6AB, + 0x5D9F8D248644CF9B, 0xFD5E4536C5662658, 0xF1C6B9FE9BACBDFD, 0xEACD6341BE9979C4, + 0xEFA7221708405576, 0x510771ECD88E543E, 0xC2BA51CB671F043D, 0x0AD482AC71AF5879, + 0xFE787A045CDAC936, 0xB238AF338E049AED, 0xBD866CC94972EE26, 0x615DA6EBBD810290, + 0x3295FDD08B2C1711, 0xF834046073BF0AEA, 0xF3099329758FFC42, 0x1CAEB13E7DCFA934, + 0xBA2307481188832B, 0x24EFCE42874CE65C, 0x0E57D61FB0E9DA1A, 0xB3D1BAD6F99B343C, + 0xC0757B1C893C4582, 0x2B510DB8403A9297, 0x5C7698C1F1DB614A, 0x3E0D0118D5E68CB4, + 0xD60F488E855CB4CF, 0xAE961E0DF3CB33D9, 0x3A8E55AB14A00ED7, 0x42170328623789C1, + 0x838B6DD19C946292, 0x895FEF7DED3B3AEB, 0xCFCBB8E64E4A3149, 0x064C7E642F65C3DC, + 0x3D2B3E2A4C5A63DA, 0x5BD3F340A9210C47, 0xB474D157A1615931, 0xAC5934DA1DE87266, + 0x6EE365117AF7765B, 0xC86ED36716B05C44, 0x9BA6885C201D49C5, 0xB905387A88346C45, + 0x131072C4BAB9DDFF, 0xBF49461EA751AF99, 0xD52977BC1CE05BA1, 0xB0F785E46027DB52, + 0x546D30BA6E57788C, 0x305AD707650F56AE, 0xC987C682612FF295, 0xA5AB8944F5FBC571, + 0x7ED528E759F244CA, 0x8DDCBBCE2C7DB888, 0xAA154ABE328DB1BA, 0x1E619BE993ECE88B, + 0x09F2BD9EE813B717, 0x7401AA4B285D1CB3, 0x21858F143195CAEE, 0x48C381841398D1B8, + 0xFCB750D3B2F98889, 0x39A86A998D1CE1B9, 0x1F888E0CE473465A, 0x7899568376978716, + 0x02CF2AD7EE2341BF, 0x85C713B5B3F1A14E, 0xFF916FE12B4567E7, 0x7C1A0230B7D10575, + 0x0C98FCC85ECA9BA5, 0xA3E7F720DA9E06AD, 0x6A6031A2BBB1F438, 0x973E74947ED7D260, + 0x2CF4663918C0FF9A, 0x5F50A7F368678E24, 0x34D983B4A449D4CD, 0x68AF1B755592B587, + 0x7F3C3D022E6DEA1B, 0xABFC5F5B45121F6B, 0x0D71E92D29553574, 0xDFFDF5106D4F03D8, + 0x081BA87B9F8C19C6, 0xDB7EA1A3AC0981BB, 0xBBCA12AD66172DFA, 0x79704366010829C7, + 0x179326777BFF5F9C, 0x0000000000000000, 0xEB2476A4C906D715, 0x724DD42F0738DF6F, + 0xB752EE6538DDB65F, 0x37FFBC863DF53BA3, 0x8EFA84FCB5C157E6, 0xE9EB5C73272596AA, + 0x1B0BDABF2535C439, 0x86E12C872A4D4E20, 0x9969A28BCE3E087A, 0xFAFB2EB79D9C4B55, + 0x056A4156B6D92CB2, 0x5A3AE6A5DEBEA296, 0x22A3B026A8292580, 0x53C85B3B36AD1581, + 0xB11E900117B87583, 0xC51F3A4A3FE56930, 0xE019E1EDCF3621BD, 0xEC811D2591FCBA18, + 0x445B7D4C4D524A1D, 0xA8DA6069DCAEF005, 0x58F5CC72309DE329, 0xD4C062596B7FF570, + 0xCE22AD0339D59F98, 0x591CD99747024DF8, 0x8B90C5AA03187B54, 0xF663D27FC356D0F0, + 0xD8589E9135B56ED5, 0x35309651D3D67A1C, 0x12F96721CD26732E, 0xD28C1C3D441A36AC, + 0x492A946164077F69, 0x2D1D73DC6F5F514B, 0x6F0A70F40D68D88A, 0x60B4B30ECA1EAC41, + 0xD36509D83385987D, 0x0B3D97490630F6A8, 0x9ECCC90A96C46577, 0xA20EE2C5AD01A87C, + 0xE49AB55E0E70A3DE, 0xA4429CA182646BA0, 0xDA97B446DB962F6A, 0xCCED87D4D7F6DE27, + 0x2AB8185D37A53C46, 0x9F25DCEFE15BCBA6, 0xC19C6EF9FEA3EB53, 0xA764A3931BD884CE, + 0x2FD2590B817C10F4, 0x56A21A6D80743933, 0xE573A0BB79EF0D0F, 0x155C0CA095DC1E23, + 0x6C2C4FC694D437E4, 0x10364DF623053291, 0xDD32DFC7836C4267, 0x03263F3299BCEF6E, + 0x66F8CD6AE57B6F9D, 0x8C35AE2B5BE21659, 0x31B3C2E21290F87F, 0x93BD2027BF915003, + 0x69460E90220D1B56, 0x299E276FAE19D328, 0x63928C3C53A2432F, 0x7082FEF8E91B9ED0, + 0xBC6F792C3EED40F7, 0x4C40D537D2DE53DB, 0x75E8BFAE5FC2B262, 0x4DA9C0D2A541FD0A, + 0x4E8FFFE03CFD1264, 0x2620E495696FA7E3, 0xE1F0F408B8A98F6C, 0xD1AA230FDDA6D9C2, + 0xC7D0109DD1C6288F, 0x8A79D04F7487D585, 0x4694579BA3710BA2, 0x38417F7CFA834F68, + 0x1D47A4DB0A5007E5, 0x206C9AF1460A643F, 0xA128DDF734BD4712, 0x8144470672B7232D, + 0xF2E086CC02105293, 0x182DE58DBC892B57, 0xCAA1F9B0F8931DFB, 0x6B892447CC2E5AE9, + 0xF9DD11850420A43B, 0x4BE5BEB68A243ED6, 0x5584255F19C8D65D, 0x3B67404E633FA006, + 0xA68DB6766C472A1F, 0xF78AC79AB4C97E21, 0xC353442E1080AAEC, 0x9A4F9DB95782E714 + }; + static const uint64_t T3[256] = + { + 0x05BA7BC82C9B3220, 0x31A54665F8B65E4F, 0xB1B651F77547F4D4, 0x8BFA0D857BA46682, + 0x85A96C5AA16A98BB, 0x990FAEF908EB79C9, 0xA15E37A247F4A62D, 0x76857DCD5D27741E, + 0xF8C50B800A1820BC, 0xBE65DCB201F7A2B4, 0x666D1B986F9426E7, 0x4CC921BF53C4E648, + 0x95410A0F93D9CA42, 0x20CDCCAA647BA4EF, 0x429A4060890A1871, 0x0C4EA4F69B32B38B, + 0xCCDA362DDE354CD3, 0x96DC23BC7C5B2FA9, 0xC309BB68AA851AB3, 0xD26131A73648E013, + 0x021DC52941FC4DB2, 0xCD5ADAB7704BE48A, 0xA77965D984ED71E6, 0x32386FD61734BBA4, + 0xE82D6DD538AB7245, 0x5C2147EA6177B4B1, 0x5DA1AB70CF091CE8, 0xAC907FCE72B8BDFF, + 0x57C85DFD972278A8, 0xA4E44C6A6B6F940D, 0x3851995B4F1FDFE4, 0x62578CCAED71BC9E, + 0xD9882BB0C01D2C0A, 0x917B9D5D113C503B, 0xA2C31E11A87643C6, 0xE463C923A399C1CE, + 0xF71686C57EA876DC, 0x87B4A973E096D509, 0xAF0D567D9D3A5814, 0xB40C2A3F59DCC6F4, + 0x3602F88495D121DD, 0xD3E1DD3D9836484A, 0xF945E71AA46688E5, 0x7518547EB2A591F5, + 0x9366587450C01D89, 0x9EA81018658C065B, 0x4F54080CBC4603A3, 0x2D0384C65137BF3D, + 0xDC325078EC861E2A, 0xEA30A8FC79573FF7, 0x214D2030CA050CB6, 0x65F0322B8016C30C, + 0x69BE96DD1B247087, 0xDB95EE9981E161B8, 0xD1FC1814D9CA05F8, 0x820ED2BBCC0DE729, + 0x63D76050430F14C7, 0x3BCCB0E8A09D3A0F, 0x8E40764D573F54A2, 0x39D175C1E16177BD, + 0x12F5A37C734F1F4B, 0xAB37C12F1FDFC26D, 0x5648B167395CD0F1, 0x6C04ED1537BF42A7, + 0xED97161D14304065, 0x7D6C67DAAB72B807, 0xEC17FA87BA4EE83C, 0xDFAF79CB0304FBC1, + 0x733F060571BC463E, 0x78D61C1287E98A27, 0xD07CF48E77B4ADA1, 0xB9C262536C90DD26, + 0xE2449B5860801605, 0x8FC09AD7F941FCFB, 0xFAD8CEA94BE46D0E, 0xA343F28B0608EB9F, + 0x9B126BD04917347B, 0x9A92874AE7699C22, 0x1B017C42C4E69EE0, 0x3A4C5C720EE39256, + 0x4B6E9F5E3EA399DA, 0x6BA353F45AD83D35, 0xE7FEE0904C1B2425, 0x22D009832587E95D, + 0x842980C00F1430E2, 0xC6B3C0A0861E2893, 0x087433A419D729F2, 0x341F3DADD42D6C6F, + 0xEE0A3FAEFBB2A58E, 0x4AEE73C490DD3183, 0xAAB72DB5B1A16A34, 0xA92A04065E238FDF, + 0x7B4B35A1686B6FCC, 0x6A23BF6EF4A6956C, 0x191CB96B851AD352, 0x55D598D4D6DE351A, + 0xC9604DE5F2AE7EF3, 0x1CA6C2A3A981E172, 0xDE2F9551AD7A5398, 0x3025AAFF56C8F616, + 0x15521D9D1E2860D9, 0x506FE31CFA45073A, 0x189C55F12B647B0B, 0x0180EC9AAE7EA859, + 0x7CEC8B40050C105E, 0x2350E5198BF94104, 0xEF8AD33455CC0DD7, 0x07A7BEE16D677F92, + 0xE5E325B90DE76997, 0x5A061591A26E637A, 0xB611EF1618208B46, 0x09F4DF3EB7A981AB, + 0x1EBB078AE87DACC0, 0xB791038CB65E231F, 0x0FD38D4574B05660, 0x67EDF702C1EA8EBE, + 0xBA5F4BE0831238CD, 0xE3C477C2CEFEBE5C, 0x0DCE486C354C1BD2, 0x8C5DB36416C31910, + 0x26EA9ED1A7627324, 0x039D29B3EF82E5EB, 0x9F28FC82CBF2AE02, 0xA8AAE89CF05D2786, + 0x431AACFA2774B028, 0xCF471F9E31B7A938, 0x581BD0B8E3922EC8, 0xBC78199B400BEF06, + 0x90FB71C7BF42F862, 0x1F3BEB1046030499, 0x683E7A47B55AD8DE, 0x988F4263A695D190, + 0xD808C72A6E638453, 0x0627527BC319D7CB, 0xEBB04466D72997AE, 0xE67E0C0AE2658C7C, + 0x14D2F107B056C880, 0x7122C32C30400B8C, 0x8A7AE11FD5DACEDB, 0xA0DEDB38E98A0E74, + 0xAD109354DCC615A6, 0x0BE91A17F655CC19, 0x8DDD5FFEB8BDB149, 0xBFE53028AF890AED, + 0xD65BA6F5B4AD7A6A, 0x7956F0882997227E, 0x10E8665532B352F9, 0x0E5361DFDACEFE39, + 0xCEC7F3049FC90161, 0xFF62B561677F5F2E, 0x975CCF26D22587F0, 0x51EF0F86543BAF63, + 0x2F1E41EF10CBF28F, 0x52722635BBB94A88, 0xAE8DBAE73344F04D, 0x410769D36688FD9A, + 0xB3AB94DE34BBB966, 0x801317928DF1AA9B, 0xA564A0F0C5113C54, 0xF131D4BEBDB1A117, + 0x7F71A2F3EA8EF5B5, 0x40878549C8F655C3, 0x7EF14E6944F05DEC, 0xD44663DCF55137D8, + 0xF2ACFD0D523344FC, 0x0000000000000000, 0x5FBC6E598EF5515A, 0x16CF342EF1AA8532, + 0xB036BD6DDB395C8D, 0x13754FE6DD31B712, 0xBBDFA77A2D6C9094, 0x89E7C8AC3A582B30, + 0x3C6B0E09CDFA459D, 0xC4AE0589C7E26521, 0x49735A777F5FD468, 0xCAFD64561D2C9B18, + 0xDA1502032F9FC9E1, 0x8867243694268369, 0x3782141E3BAF8984, 0x9CB5D53124704BE9, + 0xD7DB4A6F1AD3D233, 0xA6F989432A93D9BF, 0x9D3539AB8A0EE3B0, 0x53F2CAAF15C7E2D1, + 0x6E19283C76430F15, 0x3DEBE2936384EDC4, 0x5E3C82C3208BF903, 0x33B8834CB94A13FD, + 0x6470DEB12E686B55, 0x359FD1377A53C436, 0x61CAA57902F35975, 0x043A975282E59A79, + 0xFD7F70482683129C, 0xC52EE913699CCD78, 0x28B9FF0E7DAC8D1D, 0x5455744E78A09D43, + 0xCB7D88CCB3523341, 0x44BD121B4A13CFBA, 0x4D49CD25FDBA4E11, 0x3E76CB208C06082F, + 0x3FF627BA2278A076, 0xC28957F204FBB2EA, 0x453DFE81E46D67E3, 0x94C1E6953DA7621B, + 0x2C83685CFF491764, 0xF32C1197FC4DECA5, 0x2B24D6BD922E68F6, 0xB22B78449AC5113F, + 0x48F3B6EDD1217C31, 0x2E9EAD75BEB55AD6, 0x174FD8B45FD42D6B, 0x4ED4E4961238ABFA, + 0x92E6B4EEFEBEB5D0, 0x46A0D7320BEF8208, 0x47203BA8A5912A51, 0x24F75BF8E69E3E96, + 0xF0B1382413CF094E, 0xFEE259FBC901F777, 0x276A724B091CDB7D, 0xBDF8F501EE75475F, + 0x599B3C224DEC8691, 0x6D84018F99C1EAFE, 0x7498B8E41CDB39AC, 0xE0595E71217C5BB7, + 0x2AA43A273C50C0AF, 0xF50B43EC3F543B6E, 0x838E3E2162734F70, 0xC09492DB4507FF58, + 0x72BFEA9FDFC2EE67, 0x11688ACF9CCDFAA0, 0x1A8190D86A9836B9, 0x7ACBD93BC615C795, + 0xC7332C3A286080CA, 0x863445E94EE87D50, 0xF6966A5FD0D6DE85, 0xE9AD814F96D5DA1C, + 0x70A22FB69E3EA3D5, 0x0A69F68D582B6440, 0xB8428EC9C2EE757F, 0x604A49E3AC8DF12C, + 0x5B86F90B0C10CB23, 0xE1D9B2EB8F02F3EE, 0x29391394D3D22544, 0xC8E0A17F5CD0D6AA, + 0xB58CC6A5F7A26EAD, 0x8193FB08238F02C2, 0xD5C68F465B2F9F81, 0xFCFF9CD288FDBAC5, + 0x77059157F359DC47, 0x1D262E3907FF492B, 0xFB582233E59AC557, 0xDDB2BCE242F8B673, + 0x2577B76248E096CF, 0x6F99C4A6D83DA74C, 0xC1147E41EB795701, 0xF48BAF76912A9337 + }; + static const uint64_t T4[256] = + { + 0x3EF29D249B2C0A19, 0xE9E16322B6F8622F, 0x5536994047757F7A, 0x9F4D56D5A47B0B33, + 0x822567466AA1174C, 0xB8F5057DEB082FB2, 0xCC48C10BF4475F53, 0x373088D4275DEC3A, + 0x968F4325180AED10, 0x173D232CF7016151, 0xAE4ED09F946FCC13, 0xFD4B4741C4539873, + 0x1B5B3F0DD9933765, 0x2FFCB0967B644052, 0xE02376D20A89840C, 0xA3AE3A70329B18D7, + 0x419CBD2335DE8526, 0xFAFEBF115B7C3199, 0x0397074F85AA9B0D, 0xC58AD4FB4836B970, + 0xBEC60BE3FC4104A8, 0x1EFF36DC4B708772, 0x131FDC33ED8453B6, 0x0844E33E341764D3, + 0x0FF11B6EAB38CD39, 0x64351F0A7761B85A, 0x3B5694F509CFBA0E, 0x30857084B87245D0, + 0x47AFB3BD2297AE3C, 0xF2BA5C2F6F6B554A, 0x74BDC4761F4F70E1, 0xCFDFC64471EDC45E, + 0xE610784C1DC0AF16, 0x7ACA29D63C113F28, 0x2DED411776A859AF, 0xAC5F211E99A3D5EE, + 0xD484F949A87EF33B, 0x3CE36CA596E013E4, 0xD120F0983A9D432C, 0x6BC40464DC597563, + 0x69D5F5E5D1956C9E, 0x9AE95F043698BB24, 0xC9ECC8DA66A4EF44, 0xD69508C8A5B2EAC6, + 0xC40C2235C0503B80, 0x38C193BA8C652103, 0x1CEEC75D46BC9E8F, 0xD331011937515AD1, + 0xD8E2E56886ECA50F, 0xB137108D5779C991, 0x709F3B6905CA4206, 0x4FEB50831680CAEF, + 0xEC456AF3241BD238, 0x58D673AFE181ABBE, 0x242F54E7CAD9BF8C, 0x0211F1810DCC19FD, + 0x90BC4DBB0F43C60A, 0x9518446A9DA0761D, 0xA1BFCBF13F57012A, 0x2BDE4F8961E172B5, + 0x27B853A84F732481, 0xB0B1E643DF1F4B61, 0x18CC38425C39AC68, 0xD2B7F7D7BF37D821, + 0x3103864A3014C720, 0x14AA246372ABFA5C, 0x6E600DB54EBAC574, 0x394765740403A3F3, + 0x09C215F0BC71E623, 0x2A58B947E987F045, 0x7B4CDF18B477BDD8, 0x9709B5EB906C6FE0, + 0x73083C268060D90B, 0xFEDC400E41F9037E, 0x284948C6E44BE9B8, 0x728ECAE808065BFB, + 0x06330E9E17492B1A, 0x5950856169E7294E, 0xBAE4F4FCE6C4364F, 0xCA7BCF95E30E7449, + 0x7D7FD186A33E96C2, 0x52836110D85AD690, 0x4DFAA1021B4CD312, 0x913ABB75872544FA, + 0xDD46ECB9140F1518, 0x3D659A6B1E869114, 0xC23F2CABD719109A, 0xD713FE062DD46836, + 0xD0A60656B2FBC1DC, 0x221C5A79DD909496, 0xEFD26DBCA1B14935, 0x0E77EDA0235E4FC9, + 0xCBFD395B6B68F6B9, 0x0DE0EAEFA6F4D4C4, 0x0422FF1F1A8532E7, 0xF969B85EDED6AA94, + 0x7F6E2007AEF28F3F, 0x3AD0623B81A938FE, 0x6624EE8B7AADA1A7, 0xB682E8DDC856607B, + 0xA78CC56F281E2A30, 0xC79B257A45FAA08D, 0x5B4174E0642B30B3, 0x5F638BFF7EAE0254, + 0x4BC9AF9C0C05F808, 0xCE59308AF98B46AE, 0x8FC58DA9CC55C388, 0x803496C7676D0EB1, + 0xF33CAAE1E70DD7BA, 0xBB6202326EA2B4BF, 0xD5020F87201871CB, 0x9D5CA754A9B712CE, + 0x841669D87DE83C56, 0x8A6184785EB6739F, 0x420BBA6CB0741E2B, 0xF12D5B60EAC1CE47, + 0x76AC35F71283691C, 0x2C6BB7D9FECEDB5F, 0xFCCDB18F4C351A83, 0x1F79C012C3160582, + 0xF0ABADAE62A74CB7, 0xE1A5801C82EF06FC, 0x67A21845F2CB2357, 0x5114665F5DF04D9D, + 0xBF40FD2D74278658, 0xA0393D3FB73183DA, 0x05A409D192E3B017, 0xA9FB28CF0B4065F9, + 0x25A9A22942BF3D7C, 0xDB75E22703463E02, 0xB326E10C5AB5D06C, 0xE7968E8295A62DE6, + 0xB973F3B3636EAD42, 0xDF571D3819C30CE5, 0xEE549B7229D7CBC5, 0x12992AFD65E2D146, + 0xF8EF4E9056B02864, 0xB7041E134030E28B, 0xC02EDD2ADAD50967, 0x932B4AF48AE95D07, + 0x6FE6FB7BC6DC4784, 0x239AACB755F61666, 0x401A4BEDBDB807D6, 0x485EA8D389AF6305, + 0xA41BC220ADB4B13D, 0x753B32B89729F211, 0x997E584BB3322029, 0x1D683193CEDA1C7F, + 0xFF5AB6C0C99F818E, 0x16BBD5E27F67E3A1, 0xA59D34EE25D233CD, 0x98F8AE853B54A2D9, + 0x6DF70AFACB105E79, 0x795D2E99B9BBA425, 0x8E437B6744334178, 0x0186F6CE886682F0, + 0xEBF092A3BB347BD2, 0xBCD7FA62F18D1D55, 0xADD9D7D011C5571E, 0x0BD3E471B1BDFFDE, + 0xAA6C2F808EEAFEF4, 0x5EE57D31F6C880A4, 0xF50FA47FF044FCA0, 0x1ADDC9C351F5B595, + 0xEA76646D3352F922, 0x0000000000000000, 0x85909F16F58EBEA6, 0x46294573AAF12CCC, + 0x0A5512BF39DB7D2E, 0x78DBD85731DD26D5, 0x29CFBE086C2D6B48, 0x218B5D36583A0F9B, + 0x152CD2ADFACD78AC, 0x83A39188E2C795BC, 0xC3B9DA655F7F926A, 0x9ECBA01B2C1D89C3, + 0x07B5F8509F2FA9EA, 0x7EE8D6C926940DCF, 0x36B67E1AAF3B6ECA, 0x86079859702425AB, + 0xFB7849DFD31AB369, 0x4C7C57CC932A51E2, 0xD96413A60E8A27FF, 0x263EA566C715A671, + 0x6C71FC344376DC89, 0x4A4F595284637AF8, 0xDAF314E98B20BCF2, 0x572768C14AB96687, + 0x1088DB7C682EC8BB, 0x887075F9537A6A62, 0x2E7A4658F302C2A2, 0x619116DBE582084D, + 0xA87DDE018326E709, 0xDCC01A779C6997E8, 0xEDC39C3DAC7D50C8, 0xA60A33A1A078A8C0, + 0xC1A82BE452B38B97, 0x3F746BEA134A88E9, 0xA228CCBEBAFD9A27, 0xABEAD94E068C7C04, + 0xF48952B178227E50, 0x5CF48CB0FB049959, 0x6017E0156DE48ABD, 0x4438B4F2A73D3531, + 0x8C528AE649FF5885, 0xB515EF924DFCFB76, 0x0C661C212E925634, 0xB493195CC59A7986, + 0x9CDA519A21D1903E, 0x32948105B5BE5C2D, 0x194ACE8CD45F2E98, 0x438D4CA238129CDB, + 0x9B6FA9CABEFE39D4, 0x81B26009EF0B8C41, 0xDED1EBF691A58E15, 0x4E6DA64D9EE6481F, + 0x54B06F8ECF13FD8A, 0x49D85E1D01C9E1F5, 0xAFC826511C094EE3, 0xF698A33075EE67AD, + 0x5AC7822EEC4DB243, 0x8DD47C28C199DA75, 0x89F68337DB1CE892, 0xCDCE37C57C21DDA3, + 0x530597DE503C5460, 0x6A42F2AA543FF793, 0x5D727A7E73621BA9, 0xE232875307459DF1, + 0x56A19E0FC2DFE477, 0xC61DD3B4CD9C227D, 0xE5877F03986A341B, 0x949EB2A415C6F4ED, + 0x6206119460289340, 0x6380E75AE84E11B0, 0x8BE772B6D6D0F16F, 0x50929091D596CF6D, + 0xE86795EC3E9EE0DF, 0x7CF927482B581432, 0xC86A3E14EEC26DB4, 0x7119CDA78DACC0F6, + 0xE40189CD100CB6EB, 0x92ADBC3A028FDFF7, 0xB2A017C2D2D3529C, 0x200DABF8D05C8D6B, + 0x34A78F9BA2F77737, 0xE3B4719D8F231F01, 0x45BE423C2F5BB7C1, 0xF71E55FEFD88E55D, + 0x6853032B59F3EE6E, 0x65B3E9C4FF073AAA, 0x772AC3399AE5EBEC, 0x87816E97F842A75B, + 0x110E2DB2E0484A4B, 0x331277CB3DD8DEDD, 0xBD510CAC79EB9FA5, 0x352179552A91F5C7 + }; + static const uint64_t T5[256] = + { + 0x8AB0A96846E06A6D, 0x43C7E80B4BF0B33A, 0x08C9B3546B161EE5, 0x39F1C235EBA990BE, + 0xC1BEF2376606C7B2, 0x2C209233614569AA, 0xEB01523B6FC3289A, 0x946953AB935ACEDD, + 0x272838F63E13340E, 0x8B0455ECA12BA052, 0x77A1B2C4978FF8A2, 0xA55122CA13E54086, + 0x2276135862D3F1CD, 0xDB8DDFDE08B76CFE, 0x5D1E12C89E4A178A, 0x0E56816B03969867, + 0xEE5F79953303ED59, 0xAFED748BAB78D71D, 0x6D929F2DF93E53EE, 0xF5D8A8F8BA798C2A, + 0xF619B1698E39CF6B, 0x95DDAF2F749104E2, 0xEC2A9C80E0886427, 0xCE5C8FD8825B95EA, + 0xC4E0D9993AC60271, 0x4699C3A5173076F9, 0x3D1B151F50A29F42, 0x9ED505EA2BC75946, + 0x34665ACFDC7F4B98, 0x61B1FB53292342F7, 0xC721C0080E864130, 0x8693CD1696FD7B74, + 0x872731927136B14B, 0xD3446C8A63A1721B, 0x669A35E8A6680E4A, 0xCAB658F239509A16, + 0xA4E5DE4EF42E8AB9, 0x37A7435EE83F08D9, 0x134E6239E26C7F96, 0x82791A3C2DF67488, + 0x3F6EF00A8329163C, 0x8E5A7E42FDEB6591, 0x5CAAEE4C7981DDB5, 0x19F234785AF1E80D, + 0x255DDDE3ED98BD70, 0x50898A32A99CCCAC, 0x28CA4519DA4E6656, 0xAE59880F4CB31D22, + 0x0D9798FA37D6DB26, 0x32F968F0B4FFCD1A, 0xA00F09644F258545, 0xFA3AD5175E24DE72, + 0xF46C547C5DB24615, 0x713E80FBFF0F7E20, 0x7843CF2B73D2AAFA, 0xBD17EA36AEDF62B4, + 0xFD111BACD16F92CF, 0x4ABAA7DBC72D67E0, 0xB3416B5DAD49FAD3, 0xBCA316B24914A88B, + 0x15D150068AECF914, 0xE27C1DEBE31EFC40, 0x4FE48C759BEDA223, 0x7EDCFD141B522C78, + 0x4E5070F17C26681C, 0xE696CAC15815F3BC, 0x35D2A64B3BB481A7, 0x800CFF29FE7DFDF6, + 0x1ED9FAC3D5BAA4B0, 0x6C2663A91EF599D1, 0x03C1199134404341, 0xF7AD4DED69F20554, + 0xCD9D9649B61BD6AB, 0xC8C3BDE7EADB1368, 0xD131899FB02AFB65, 0x1D18E352E1FAE7F1, + 0xDA39235AEF7CA6C1, 0xA1BBF5E0A8EE4F7A, 0x91377805CF9A0B1E, 0x3138716180BF8E5B, + 0xD9F83ACBDB3CE580, 0x0275E515D38B897E, 0x472D3F21F0FBBCC6, 0x2D946EB7868EA395, + 0xBA3C248D21942E09, 0xE7223645BFDE3983, 0xFF64FEB902E41BB1, 0xC97741630D10D957, + 0xC3CB1722B58D4ECC, 0xA27AEC719CAE0C3B, 0x99FECB51A48C15FB, 0x1465AC826D27332B, + 0xE1BD047AD75EBF01, 0x79F733AF941960C5, 0x672EC96C41A3C475, 0xC27FEBA6524684F3, + 0x64EFD0FD75E38734, 0xED9E60040743AE18, 0xFB8E2993B9EF144D, 0x38453EB10C625A81, + 0x6978480742355C12, 0x48CF42CE14A6EE9E, 0x1CAC1FD606312DCE, 0x7B82D6BA4792E9BB, + 0x9D141C7B1F871A07, 0x5616B80DC11C4A2E, 0xB849C198F21FA777, 0x7CA91801C8D9A506, + 0xB1348E487EC273AD, 0x41B20D1E987B3A44, 0x7460AB55A3CFBBE3, 0x84E628034576F20A, + 0x1B87D16D897A6173, 0x0FE27DEFE45D5258, 0x83CDE6B8CA3DBEB7, 0x0C23647ED01D1119, + 0x7A362A3EA0592384, 0xB61F40F3F1893F10, 0x75D457D1440471DC, 0x4558DA34237035B8, + 0xDCA6116587FC2043, 0x8D9B67D3C9AB26D0, 0x2B0B5C88EE0E2517, 0x6FE77A382AB5DA90, + 0x269CC472D9D8FE31, 0x63C41E46FAA8CB89, 0xB7ABBC771642F52F, 0x7D1DE4852F126F39, + 0xA8C6BA3024339BA0, 0x600507D7CEE888C8, 0x8FEE82C61A20AFAE, 0x57A2448926D78011, + 0xFCA5E72836A458F0, 0x072BCEBB8F4B4CBD, 0x497BBE4AF36D24A1, 0x3CAFE99BB769557D, + 0x12FA9EBD05A7B5A9, 0xE8C04BAA5B836BDB, 0x4273148FAC3B7905, 0x908384812851C121, + 0xE557D3506C55B0FD, 0x72FF996ACB4F3D61, 0x3EDA0C8E64E2DC03, 0xF0868356E6B949E9, + 0x04EAD72ABB0B0FFC, 0x17A4B5135967706A, 0xE3C8E16F04D5367F, 0xF84F30028DAF570C, + 0x1846C8FCBD3A2232, 0x5B8120F7F6CA9108, 0xD46FA231ECEA3EA6, 0x334D947453340725, + 0x58403966C28AD249, 0xBED6F3A79A9F21F5, 0x68CCB483A5FE962D, 0xD085751B57E1315A, + 0xFED0023DE52FD18E, 0x4B0E5B5F20E6ADDF, 0x1A332DE96EB1AB4C, 0xA3CE10F57B65C604, + 0x108F7BA8D62C3CD7, 0xAB07A3A11073D8E1, 0x6B0DAD1291BED56C, 0xF2F366433532C097, + 0x2E557726B2CEE0D4, 0x0000000000000000, 0xCB02A476DE9B5029, 0xE4E32FD48B9E7AC2, + 0x734B65EE2C84F75E, 0x6E5386BCCD7E10AF, 0x01B4FC84E7CBCA3F, 0xCFE8735C65905FD5, + 0x3613BFDA0FF4C2E6, 0x113B872C31E7F6E8, 0x2FE18BA255052AEB, 0xE974B72EBC48A1E4, + 0x0ABC5641B89D979B, 0xB46AA5E62202B66E, 0x44EC26B0C4BBFF87, 0xA6903B5B27A503C7, + 0x7F680190FC99E647, 0x97A84A3AA71A8D9C, 0xDD12EDE16037EA7C, 0xC554251DDD0DC84E, + 0x88C54C7D956BE313, 0x4D91696048662B5D, 0xB08072CC9909B992, 0xB5DE5962C5C97C51, + 0x81B803AD19B637C9, 0xB2F597D94A8230EC, 0x0B08AAC55F565DA4, 0xF1327FD2017283D6, + 0xAD98919E78F35E63, 0x6AB9519676751F53, 0x24E921670A53774F, 0xB9FD3D1C15D46D48, + 0x92F66194FBDA485F, 0x5A35DC7311015B37, 0xDED3F4705477A93D, 0xC00A0EB381CD0D8D, + 0xBB88D809C65FE436, 0x16104997BEACBA55, 0x21B70AC95693B28C, 0x59F4C5E225411876, + 0xD5DB5EB50B21F499, 0x55D7A19CF55C096F, 0xA97246B4C3F8519F, 0x8552D487A2BD3835, + 0x54635D181297C350, 0x23C2EFDC85183BF2, 0x9F61F96ECC0C9379, 0x534893A39DDC8FED, + 0x5EDF0B59AA0A54CB, 0xAC2C6D1A9F38945C, 0xD7AEBBA0D8AA7DE7, 0x2ABFA00C09C5EF28, + 0xD84CC64F3CF72FBF, 0x2003F64DB15878B3, 0xA724C7DFC06EC9F8, 0x069F323F68808682, + 0xCC296ACD51D01C94, 0x055E2BAE5CC0C5C3, 0x6270E2C21D6301B6, 0x3B842720382219C0, + 0xD2F0900E846AB824, 0x52FC6F277A1745D2, 0xC6953C8CE94D8B0F, 0xE009F8FE3095753E, + 0x655B2C7992284D0B, 0x984A37D54347DFC4, 0xEAB5AEBF8808E2A5, 0x9A3FD2C090CC56BA, + 0x9CA0E0FFF84CD038, 0x4C2595E4AFADE162, 0xDF6708F4B3BC6302, 0xBF620F237D54EBCA, + 0x93429D101C118260, 0x097D4FD08CDDD4DA, 0x8C2F9B572E60ECEF, 0x708A7C7F18C4B41F, + 0x3A30DBA4DFE9D3FF, 0x4006F19A7FB0F07B, 0x5F6BF7DD4DC19EF4, 0x1F6D064732716E8F, + 0xF9FBCC866A649D33, 0x308C8DE567744464, 0x8971B0F972A0292C, 0xD61A47243F61B7D8, + 0xEFEB8511D4C82766, 0x961CB6BE40D147A3, 0xAAB35F25F7B812DE, 0x76154E407044329D, + 0x513D76B64E570693, 0xF3479AC7D2F90AA8, 0x9B8B2E4477079C85, 0x297EB99D3D85AC69 + }; + static const uint64_t T6[256] = + { + 0x7E37E62DFC7D40C3, 0x776F25A4EE939E5B, 0xE045C850DD8FB5AD, 0x86ED5BA711FF1952, + 0xE91D0BD9CF616B35, 0x37E0AB256E408FFB, 0x9607F6C031025A7A, 0x0B02F5E116D23C9D, + 0xF3D8486BFB50650C, 0x621CFF27C40875F5, 0x7D40CB71FA5FD34A, 0x6DAA6616DAA29062, + 0x9F5F354923EC84E2, 0xEC847C3DC507C3B3, 0x025A3668043CE205, 0xA8BF9E6C4DAC0B19, + 0xFA808BE2E9BEBB94, 0xB5B99C5277C74FA3, 0x78D9BC95F0397BCC, 0xE332E50CDBAD2624, + 0xC74FCE129332797E, 0x1729ECEB2EA709AB, 0xC2D6B9F69954D1F8, 0x5D898CBFBAB8551A, + 0x859A76FB17DD8ADB, 0x1BE85886362F7FB5, 0xF6413F8FF136CD8A, 0xD3110FA5BBB7E35C, + 0x0A2FEED514CC4D11, 0xE83010EDCD7F1AB9, 0xA1E75DE55F42D581, 0xEEDE4A55C13B21B6, + 0xF2F5535FF94E1480, 0x0CC1B46D1888761E, 0xBCE15FDB6529913B, 0x2D25E8975A7181C2, + 0x71817F1CE2D7A554, 0x2E52C5CB5C53124B, 0xF9F7A6BEEF9C281D, 0x9E722E7D21F2F56E, + 0xCE170D9B81DCA7E6, 0x0E9B82051CB4941B, 0x1E712F623C49D733, 0x21E45CFA42F9F7DC, + 0xCB8E7A7F8BBA0F60, 0x8E98831A010FB646, 0x474CCF0D8E895B23, 0xA99285584FB27A95, + 0x8CC2B57205335443, 0x42D5B8E984EFF3A5, 0x012D1B34021E718C, 0x57A6626AAE74180B, + 0xFF19FC06E3D81312, 0x35BA9D4D6A7C6DFE, 0xC9D44C178F86ED65, 0x506523E6A02E5288, + 0x03772D5C06229389, 0x8B01F4FE0B691EC0, 0xF8DABD8AED825991, 0x4C4E3AEC985B67BE, + 0xB10DF0827FBF96A9, 0x6A69279AD4F8DAE1, 0xE78689DCD3D5FF2E, 0x812E1A2B1FA553D1, + 0xFBAD90D6EBA0CA18, 0x1AC543B234310E39, 0x1604F7DF2CB97827, 0xA6241C6951189F02, + 0x753513CCEAAF7C5E, 0x64F2A59FC84C4EFA, 0x247D2B1E489F5F5A, 0xDB64D718AB474C48, + 0x79F4A7A1F2270A40, 0x1573DA832A9BEBAE, 0x3497867968621C72, 0x514838D2A2302304, + 0xF0AF6537FD72F685, 0x1D06023E3A6B44BA, 0x678588C3CE6EDD73, 0x66A893F7CC70ACFF, + 0xD4D24E29B5EDA9DF, 0x3856321470EA6A6C, 0x07C3418C0E5A4A83, 0x2BCBB22F5635BACD, + 0x04B46CD00878D90A, 0x06EE5AB80C443B0F, 0x3B211F4876C8F9E5, 0x0958C38912EEDE98, + 0xD14B39CDBF8B0159, 0x397B292072F41BE0, 0x87C0409313E168DE, 0xAD26E98847CAA39F, + 0x4E140C849C6785BB, 0xD5FF551DB7F3D853, 0xA0CA46D15D5CA40D, 0xCD6020C787FE346F, + 0x84B76DCF15C3FB57, 0xDEFDA0FCA121E4CE, 0x4B8D7B6096012D3D, 0x9AC642AD298A2C64, + 0x0875D8BD10F0AF14, 0xB357C6EA7B8374AC, 0x4D6321D89A451632, 0xEDA96709C719B23F, + 0xF76C24BBF328BC06, 0xC662D526912C08F2, 0x3CE25EC47892B366, 0xB978283F6F4F39BD, + 0xC08C8F9E9D6833FD, 0x4F3917B09E79F437, 0x593DE06FB2C08C10, 0xD6887841B1D14BDA, + 0x19B26EEE32139DB0, 0xB494876675D93E2F, 0x825937771987C058, 0x90E9AC783D466175, + 0xF1827E03FF6C8709, 0x945DC0A8353EB87F, 0x4516F9658AB5B926, 0x3F9573987EB020EF, + 0xB855330B6D514831, 0x2AE6A91B542BCB41, 0x6331E413C6160479, 0x408F8E8180D311A0, + 0xEFF35161C325503A, 0xD06622F9BD9570D5, 0x8876D9A20D4B8D49, 0xA5533135573A0C8B, + 0xE168D364DF91C421, 0xF41B09E7F50A2F8F, 0x12B09B0F24C1A12D, 0xDA49CC2CA9593DC4, + 0x1F5C34563E57A6BF, 0x54D14F36A8568B82, 0xAF7CDFE043F6419A, 0xEA6A2685C943F8BC, + 0xE5DCBFB4D7E91D2B, 0xB27ADDDE799D0520, 0x6B443CAED6E6AB6D, 0x7BAE91C9F61BE845, + 0x3EB868AC7CAE5163, 0x11C7B65322E332A4, 0xD23C1491B9A992D0, 0x8FB5982E0311C7CA, + 0x70AC6428E0C9D4D8, 0x895BC2960F55FCC5, 0x76423E90EC8DEFD7, 0x6FF0507EDE9E7267, + 0x3DCF45F07A8CC2EA, 0x4AA06054941F5CB1, 0x5810FB5BB0DEFD9C, 0x5EFEA1E3BC9AC693, + 0x6EDD4B4ADC8003EB, 0x741808F8E8B10DD2, 0x145EC1B728859A22, 0x28BC9F7350172944, + 0x270A06424EBDCCD3, 0x972AEDF4331C2BF6, 0x059977E40A66A886, 0x2550302A4A812ED6, + 0xDD8A8DA0A7037747, 0xC515F87A970E9B7B, 0x3023EAA9601AC578, 0xB7E3AA3A73FBADA6, + 0x0FB699311EAAE597, 0x0000000000000000, 0x310EF19D6204B4F4, 0x229371A644DB6455, + 0x0DECAF591A960792, 0x5CA4978BB8A62496, 0x1C2B190A38753536, 0x41A295B582CD602C, + 0x3279DCC16426277D, 0xC1A194AA9F764271, 0x139D803B26DFD0A1, 0xAE51C4D441E83016, + 0xD813FA44AD65DFC1, 0xAC0BF2BC45D4D213, 0x23BE6A9246C515D9, 0x49D74D08923DCF38, + 0x9D05032127D066E7, 0x2F7FDEFF5E4D63C7, 0xA47E2A0155247D07, 0x99B16FF12FA8BFED, + 0x4661D4398C972AAF, 0xDFD0BBC8A33F9542, 0xDCA79694A51D06CB, 0xB020EBB67DA1E725, + 0xBA0F0563696DAA34, 0xE4F1A480D5F76CA7, 0xC438E34E9510EAF7, 0x939E81243B64F2FC, + 0x8DEFAE46072D25CF, 0x2C08F3A3586FF04E, 0xD7A56375B3CF3A56, 0x20C947CE40E78650, + 0x43F8A3DD86F18229, 0x568B795EAC6A6987, 0x8003011F1DBB225D, 0xF53612D3F7145E03, + 0x189F75DA300DEC3C, 0x9570DB9C3720C9F3, 0xBB221E576B73DBB8, 0x72F65240E4F536DD, + 0x443BE25188ABC8AA, 0xE21FFE38D9B357A8, 0xFD43CA6EE7E4F117, 0xCAA3614B89A47EEC, + 0xFE34E732E1C6629E, 0x83742C431B99B1D4, 0xCF3A16AF83C2D66A, 0xAAE5A8044990E91C, + 0x26271D764CA3BD5F, 0x91C4B74C3F5810F9, 0x7C6DD045F841A2C6, 0x7F1AFD19FE63314F, + 0xC8F957238D989CE9, 0xA709075D5306EE8E, 0x55FC5402AA48FA0E, 0x48FA563C9023BEB4, + 0x65DFBEABCA523F76, 0x6C877D22D8BCE1EE, 0xCC4D3BF385E045E3, 0xBEBB69B36115733E, + 0x10EAAD6720FD4328, 0xB6CEB10E71E5DC2A, 0xBDCC44EF6737E0B7, 0x523F158EA412B08D, + 0x989C74C52DB6CE61, 0x9BEB59992B945DE8, 0x8A2CEFCA09776F4C, 0xA3BD6B8D5B7E3784, + 0xEB473DB1CB5D8930, 0xC3FBA2C29B4AA074, 0x9C28181525CE176B, 0x683311F2D0C438E4, + 0x5FD3BAD7BE84B71F, 0xFC6ED15AE5FA809B, 0x36CDB0116C5EFE77, 0x29918447520958C8, + 0xA29070B959604608, 0x53120EBAA60CC101, 0x3A0C047C74D68869, 0x691E0AC6D2DA4968, + 0x73DB4974E6EB4751, 0x7A838AFDF40599C9, 0x5A4ACD33B4E21F99, 0x6046C94FC03497F0, + 0xE6AB92E8D1CB8EA2, 0x3354C7F5663856F1, 0xD93EE170AF7BAE4D, 0x616BD27BC22AE67C, + 0x92B39A10397A8370, 0xABC8B3304B8E9890, 0xBF967287630B02B2, 0x5B67D607B6FC6E15 + }; + static uint64_t T7[256] = + { + 0xD031C397CE553FE6, 0x16BA5B01B006B525, 0xA89BADE6296E70C8, 0x6A1F525D77D3435B, + 0x6E103570573DFA0B, 0x660EFB2A17FC95AB, 0x76327A9E97634BF6, 0x4BAD9D6462458BF5, + 0xF1830CAEDBC3F748, 0xC5C8F542669131FF, 0x95044A1CDC48B0CB, 0x892962DF3CF8B866, + 0xB0B9E208E930C135, 0xA14FB3F0611A767C, 0x8D2605F21C160136, 0xD6B71922FECC549E, + 0x37089438A5907D8B, 0x0B5DA38E5803D49C, 0x5A5BCC9CEA6F3CBC, 0xEDAE246D3B73FFE5, + 0xD2B87E0FDE22EDCE, 0x5E54ABB1CA8185EC, 0x1DE7F88FE80561B9, 0xAD5E1A870135A08C, + 0x2F2ADBD665CECC76, 0x5780B5A782F58358, 0x3EDC8A2EEDE47B3F, 0xC9D95C3506BEE70F, + 0x83BE111D6C4E05EE, 0xA603B90959367410, 0x103C81B4809FDE5D, 0x2C69B6027D0C774A, + 0x399080D7D5C87953, 0x09D41E16487406B4, 0xCDD63B1826505E5F, 0xF99DC2F49B0298E8, + 0x9CD0540A943CB67F, 0xBCA84B7F891F17C5, 0x723D1DB3B78DF2A6, 0x78AA6E71E73B4F2E, + 0x1433E699A071670D, 0x84F21BE454620782, 0x98DF3327B4D20F2F, 0xF049DCE2D3769E5C, + 0xDB6C60199656EB7A, 0x648746B2078B4783, 0x32CD23598DCBADCF, 0x1EA4955BF0C7DA85, + 0xE9A143401B9D46B5, 0xFD92A5D9BBEC21B8, 0xC8138C790E0B8E1B, 0x2EE00B9A6D7BA562, + 0xF85712B893B7F1FC, 0xEB28FED80BEA949D, 0x564A65EB8A40EA4C, 0x6C9988E8474A2823, + 0x4535898B121D8F2D, 0xABD8C03231ACCBF4, 0xBA2E91CAB9867CBD, 0x7960BE3DEF8E263A, + 0x0C11A977602FD6F0, 0xCB50E1AD16C93527, 0xEAE22E94035FFD89, 0x2866D12F5DE2CE1A, + 0xFF1B1841AB9BF390, 0x9F9339DE8CFE0D43, 0x964727C8C48A0BF7, 0x524502C6AAAE531C, + 0x9B9C5EF3AC10B413, 0x4FA2FA4942AB32A5, 0x3F165A62E551122B, 0xC74148DA76E6E3D7, + 0x924840E5E464B2A7, 0xD372AE43D69784DA, 0x233B72A105E11A86, 0xA48A04914941A638, + 0xB4B68525C9DE7865, 0xDDEABAACA6CF8002, 0x0A9773C250B6BD88, 0xC284FFBB5EBD3393, + 0x8BA0DF472C8F6A4E, 0x2AEF6CB74D951C32, 0x427983722A318D41, 0x73F7CDFFBF389BB2, + 0x074C0AF9382C026C, 0x8A6A0F0B243A035A, 0x6FDAE53C5F88931F, 0xC68B98967E538AC3, + 0x44FF59C71AA8E639, 0xE2FCE0CE439E9229, 0xA20CDE2479D8CD40, 0x19E89FA2C8EBD8E9, + 0xF446BBCFF398270C, 0x43B3533E2284E455, 0xD82F0DCD8E945046, 0x51066F12B26CE820, + 0xE73957AF6BC5426D, 0x081ECE5A40C16FA0, 0x3B193D4FC5BFAB7B, 0x7FE66488DF174D42, + 0x0E9814EF705804D8, 0x8137AC857C39D7C6, 0xB1733244E185A821, 0x695C3F896F11F867, + 0xF6CF0657E3EFF524, 0x1AABF276D02963D5, 0x2DA3664E75B91E5E, 0x0289BD981077D228, + 0x90C1FD7DF413608F, 0x3C5537B6FD93A917, 0xAA12107E3919A2E0, 0x0686DAB530996B78, + 0xDAA6B0559EE3826E, 0xC34E2FF756085A87, 0x6D5358A44FFF4137, 0xFC587595B35948AC, + 0x7CA5095CC7D5F67E, 0xFB147F6C8B754AC0, 0xBFEB26AB91DDACF9, 0x6896EFC567A49173, + 0xCA9A31E11E7C5C33, 0xBBE44186B13315A9, 0x0DDB793B689ABFE4, 0x70B4A02BA7FA208E, + 0xE47A3A7B7307F951, 0x8CECD5BE14A36822, 0xEEED49B923B144D9, 0x17708B4DB8B3DC31, + 0x6088219F2765FED3, 0xB3FA8FDCF1F27A09, 0x910B2D31FCA6099B, 0x0F52C4A378ED6DCC, + 0x50CCBF5EBAD98134, 0x6BD582117F662A4F, 0x94CE9A50D4FDD9DF, 0x2B25BCFB45207526, + 0x67C42B661F49FCBF, 0x492420FC723259DD, 0x03436DD418C2BB3C, 0x1F6E4517F872B391, + 0xA08563BC69AF1F68, 0xD43EA4BAEEBB86B6, 0x01CAD04C08B56914, 0xAC94CACB0980C998, + 0x54C3D8739A373864, 0x26FEC5C02DBACAC2, 0xDEA9D778BE0D3B3E, 0x040F672D20EEB950, + 0xE5B0EA377BB29045, 0xF30AB136CBB42560, 0x62019C0737122CFB, 0xE86B930C13282FA1, + 0xCC1CEB542EE5374B, 0x538FD28AA21B3A08, 0x1B61223AD89C0AC1, 0x36C24474AD25149F, + 0x7A23D3E9F74C9D06, 0xBE21F6E79968C5ED, 0xCF5F868036278C77, 0xF705D61BEB5A9C30, + 0x4D2B47D152DCE08D, 0x5F9E7BFDC234ECF8, 0x247778583DCD18EA, 0x867BA67C4415D5AA, + 0x4CE1979D5A698999, 0x0000000000000000, 0xEC64F42133C696F1, 0xB57C5569C16B1171, + 0xC1C7926F467F88AF, 0x654D96FE0F3E2E97, 0x15F936D5A8C40E19, 0xB8A72C52A9F1AE95, + 0xA9517DAA21DB19DC, 0x58D27104FA18EE94, 0x5918A148F2AD8780, 0x5CDD1629DAF657C4, + 0x8274C15164FB6CFA, 0xD1FB13DBC6E056F2, 0x7D6FD910CF609F6A, 0xB63F38BDD9A9AA4D, + 0x3D9FE7FAF526C003, 0x74BBC706871499DE, 0xDF630734B6B8522A, 0x3AD3ED03CD0AC26F, + 0xFADEAF2083C023D4, 0xC00D42234ECAE1BB, 0x8538CBA85CD76E96, 0xC402250E6E2458EB, + 0x47BC3413026A5D05, 0xAFD7A71F114272A4, 0x978DF784CC3F62E3, 0xB96DFC1EA144C781, + 0x21B2CF391596C8AE, 0x318E4E8D950916F3, 0xCE9556CC3E92E563, 0x385A509BDD7D1047, + 0x358129A0B5E7AFA3, 0xE6F387E363702B79, 0xE0755D5653E94001, 0x7BE903A5FFF9F412, + 0x12B53C2C90E80C75, 0x3307F315857EC4DB, 0x8FAFB86A0C61D31E, 0xD9E5DD8186213952, + 0x77F8AAD29FD622E2, 0x25BDA814357871FE, 0x7571174A8FA1F0CA, 0x137FEC60985D6561, + 0x30449EC19DBC7FE7, 0xA540D4DD41F4CF2C, 0xDC206AE0AE7AE916, 0x5B911CD0E2DA55A8, + 0xB2305F90F947131D, 0x344BF9ECBD52C6B7, 0x5D17C665D2433ED0, 0x18224FEEC05EB1FD, + 0x9E59E992844B6457, 0x9A568EBFA4A5DD07, 0xA3C60E68716DA454, 0x7E2CB4C4D7A22456, + 0x87B176304CA0BCBE, 0x413AEEA632F3367D, 0x9915E36BBC67663B, 0x40F03EEA3A465F69, + 0x1C2D28C3E0B008AD, 0x4E682A054A1E5BB1, 0x05C5B761285BD044, 0xE1BF8D1A5B5C2915, + 0xF2C0617AC3014C74, 0xB7F5E8F1D11CC359, 0x63CB4C4B3FA745EF, 0x9D1A84469C89DF6B, + 0xE33630824B2BFB3D, 0xD5F474F6E60EEFA2, 0xF58C6B83FB2D4E18, 0x4676E45F0ADF3411, + 0x20781F751D23A1BA, 0xBD629B3381AA7ED1, 0xAE1D775319F71BB0, 0xFED1C80DA32E9A84, + 0x5509083F92825170, 0x29AC01635557A70E, 0xA7C9694551831D04, 0x8E65682604D4BA0A, + 0x11F651F8882AB749, 0xD77DC96EF6793D8A, 0xEF2799F52B042DCD, 0x48EEF0B07A8730C9, + 0x22F1A2ED0D547392, 0x6142F1D32FD097C7, 0x4A674D286AF0E2E1, 0x80FD7CC9748CBED2, + 0x717E7067AF4F499A, 0x938290A9ECD1DBB3, 0x88E3B293344DD172, 0x2734158C250FA3D6 + }; - static const uint64_t C_[12][8] = - { - { - 0xe9daca1eda5b08b1, 0x1f7c65c0812fcbeb, 0x16d0452e43766a2f, 0xfcc485758db84e71, - 0x0169679291e07c4b, 0x15d360a4082a42a2, 0x234d74cc36747605, 0x0745a6f2596580dd - }, - { - 0x1a2f9da98ab5a36f, 0xd7b5700f469de34f, 0x982b230a72eafef3, 0x3101b5160f5ed561, - 0x5899d6126b17b59a, 0xcaa70adbc261b55c, 0x56cdcbd71ba2dd55, 0xb79bb121700479e6 - }, - { - 0xc72fce2bacdc74f5, 0x35843d6a28fc390a, 0x8b1f9c525f5ef106, 0x7b7b29b11475eaf2, - 0xb19e3590e40fe2d3, 0x09db6260373ac9c1, 0x31db7a8643f4b6c2, 0xb20aba0af5961e99 - }, - { - 0xd26615e8b3df1fef, 0xdde4715da0e148f9, 0x7d3c5c337e858e48, 0x3f355e68ad1c729d, - 0x75d603ed822cd7a9, 0xbe0352933313b7d8, 0xf137e893a1ea5334, 0x2ed1e384bcbe0c22 - }, - { - 0x994747adac6bea4b, 0x6323a96c0c413f9a, 0x4a1086161f1c157f, 0xbdff0f80d7359e35, - 0xa3f53a254717cdbf, 0x161a2723b700ffdf, 0xf563eaa97ea2567a, 0x57fe6c7cfd581760 - }, - { - 0xd9d33a1daeae4fae, 0xc039307a3bc3a46f, 0x6ca44251f9c4662d, 0xc68ef09ab49a7f18, - 0xb4b79a1cb7a6facf, 0xb6c6bec2661ff20a, 0x354f903672c571bf, 0x6e7d64467a4068fa - }, - { - 0xecc5aaee160ec7f4, 0x540924bffe86ac51, 0xc987bfe6c7c69e39, 0xc9937a19333e47d3, - 0x372c822dc5ab9209, 0x04054a2883694706, 0xf34a3ca24c451735, 0x93d4143a4d568688 - }, - { - 0xa7c9934d425b1f9b, 0x41416e0c02aae703, 0x1ede369c71f8b74e, 0x9ac4db4d3b44b489, - 0x90069b92cb2b89f4, 0x2fc4a5d12b8dd169, 0xd9a8515935c2ac36, 0x1ee702bfd40d7fa4 - }, - { - 0x9b223116545a8f37, 0xde5f16ecd89a4c94, 0x244289251b3a7d3a, 0x84090de0b755d93c, - 0xb1ceb2db0b440a80, 0x549c07a69a8a2b7b, 0x602a1fcb92dc380e, 0xdb5a238351446172 - }, - { - 0x526f0580a6debeab, 0xf3f3e4b248e52a38, 0xdb788aff1ce74189, 0x0361331b8ae1ff1f, - 0x4b3369af0267e79f, 0xf452763b306c1e7a, 0xc3b63b15d1fa9836, 0xed9c4598fbc7b474 - }, - { - 0xfb89c8efd09ecd7b, 0x94fe5a63cdc60230, 0x6107abebbb6bfad8, 0x7966841421800120, - 0xcab948eaef711d8a, 0x986e477d1dcdbaef, 0x5dd86fc04a59a2de, 0x1b2df381cda4ca6b - }, - { - 0xba3116f167e78e37, 0x7ab14904b08013d2, 0x771ddfbc323ca4cd, 0x9b9f2130d41220f8, - 0x86cc91189def805d, 0x5228e188aaa41de7, 0x991bb2d9d517f4fa, 0x20d71bf14a92bc48 - } - }; + static const uint64_t C_[12][8] = + { + { + 0xe9daca1eda5b08b1, 0x1f7c65c0812fcbeb, 0x16d0452e43766a2f, 0xfcc485758db84e71, + 0x0169679291e07c4b, 0x15d360a4082a42a2, 0x234d74cc36747605, 0x0745a6f2596580dd + }, + { + 0x1a2f9da98ab5a36f, 0xd7b5700f469de34f, 0x982b230a72eafef3, 0x3101b5160f5ed561, + 0x5899d6126b17b59a, 0xcaa70adbc261b55c, 0x56cdcbd71ba2dd55, 0xb79bb121700479e6 + }, + { + 0xc72fce2bacdc74f5, 0x35843d6a28fc390a, 0x8b1f9c525f5ef106, 0x7b7b29b11475eaf2, + 0xb19e3590e40fe2d3, 0x09db6260373ac9c1, 0x31db7a8643f4b6c2, 0xb20aba0af5961e99 + }, + { + 0xd26615e8b3df1fef, 0xdde4715da0e148f9, 0x7d3c5c337e858e48, 0x3f355e68ad1c729d, + 0x75d603ed822cd7a9, 0xbe0352933313b7d8, 0xf137e893a1ea5334, 0x2ed1e384bcbe0c22 + }, + { + 0x994747adac6bea4b, 0x6323a96c0c413f9a, 0x4a1086161f1c157f, 0xbdff0f80d7359e35, + 0xa3f53a254717cdbf, 0x161a2723b700ffdf, 0xf563eaa97ea2567a, 0x57fe6c7cfd581760 + }, + { + 0xd9d33a1daeae4fae, 0xc039307a3bc3a46f, 0x6ca44251f9c4662d, 0xc68ef09ab49a7f18, + 0xb4b79a1cb7a6facf, 0xb6c6bec2661ff20a, 0x354f903672c571bf, 0x6e7d64467a4068fa + }, + { + 0xecc5aaee160ec7f4, 0x540924bffe86ac51, 0xc987bfe6c7c69e39, 0xc9937a19333e47d3, + 0x372c822dc5ab9209, 0x04054a2883694706, 0xf34a3ca24c451735, 0x93d4143a4d568688 + }, + { + 0xa7c9934d425b1f9b, 0x41416e0c02aae703, 0x1ede369c71f8b74e, 0x9ac4db4d3b44b489, + 0x90069b92cb2b89f4, 0x2fc4a5d12b8dd169, 0xd9a8515935c2ac36, 0x1ee702bfd40d7fa4 + }, + { + 0x9b223116545a8f37, 0xde5f16ecd89a4c94, 0x244289251b3a7d3a, 0x84090de0b755d93c, + 0xb1ceb2db0b440a80, 0x549c07a69a8a2b7b, 0x602a1fcb92dc380e, 0xdb5a238351446172 + }, + { + 0x526f0580a6debeab, 0xf3f3e4b248e52a38, 0xdb788aff1ce74189, 0x0361331b8ae1ff1f, + 0x4b3369af0267e79f, 0xf452763b306c1e7a, 0xc3b63b15d1fa9836, 0xed9c4598fbc7b474 + }, + { + 0xfb89c8efd09ecd7b, 0x94fe5a63cdc60230, 0x6107abebbb6bfad8, 0x7966841421800120, + 0xcab948eaef711d8a, 0x986e477d1dcdbaef, 0x5dd86fc04a59a2de, 0x1b2df381cda4ca6b + }, + { + 0xba3116f167e78e37, 0x7ab14904b08013d2, 0x771ddfbc323ca4cd, 0x9b9f2130d41220f8, + 0x86cc91189def805d, 0x5228e188aaa41de7, 0x991bb2d9d517f4fa, 0x20d71bf14a92bc48 + } + }; - union GOST3411Block // 8 bytes aligned - { - uint8_t buf[64]; - uint64_t ll[8]; + union GOST3411Block // 8 bytes aligned + { + uint8_t buf[64]; + uint64_t ll[8]; - GOST3411Block operator^(const GOST3411Block& other) const - { - GOST3411Block ret; - for (int i = 0; i < 8; i++) - ret.ll[i] = ll[i]^other.ll[i]; - return ret; - } + GOST3411Block operator^(const GOST3411Block &other) const { + GOST3411Block ret; + for (int i = 0; i < 8; i++) + ret.ll[i] = ll[i] ^ other.ll[i]; + return ret; + } - GOST3411Block operator^(const uint64_t * other) const - { - GOST3411Block ret; - for (int i = 0; i < 8; i++) - ret.ll[i] = ll[i]^other[i]; - return ret; - } + GOST3411Block operator^(const uint64_t *other) const { + GOST3411Block ret; + for (int i = 0; i < 8; i++) + ret.ll[i] = ll[i] ^ other[i]; + return ret; + } - GOST3411Block operator+(const GOST3411Block& other) const - { - GOST3411Block ret; - uint8_t carry = 0; - for (int i = 63; i >= 0; i--) - { - uint16_t sum = buf[i] + other.buf[i] + carry; - ret.buf[i] = sum; - carry = sum >> 8; - } - return ret; - } + GOST3411Block operator+(const GOST3411Block &other) const { + GOST3411Block ret; + uint8_t carry = 0; + for (int i = 63; i >= 0; i--) { + uint16_t sum = buf[i] + other.buf[i] + carry; + ret.buf[i] = sum; + carry = sum >> 8; + } + return ret; + } - void Add (uint32_t c) - { - for (int i = 63; i >= 0; i--) - { - if (!c) return; - c += buf[i]; - buf[i] = c; - c >>= 8; - } - } + void Add(uint32_t c) { + for (int i = 63; i >= 0; i--) { + if (!c) return; + c += buf[i]; + buf[i] = c; + c >>= 8; + } + } - void F () - { - uint64_t res[8]; - for (int b=0; b<8; b++) - { - uint64_t r; - r = T0[buf[b+56]]; - r ^= T1[buf[b+48]]; - r ^= T2[buf[b+40]]; - r ^= T3[buf[b+32]]; - r ^= T4[buf[b+24]]; - r ^= T5[buf[b+16]]; - r ^= T6[buf[b+8]]; - r ^= T7[buf[b]]; - res[b] = r; - } - memcpy (buf, res, 64); - } + void F() { + uint64_t res[8]; + for (int b = 0; b < 8; b++) { + uint64_t r; + r = T0[buf[b + 56]]; + r ^= T1[buf[b + 48]]; + r ^= T2[buf[b + 40]]; + r ^= T3[buf[b + 32]]; + r ^= T4[buf[b + 24]]; + r ^= T5[buf[b + 16]]; + r ^= T6[buf[b + 8]]; + r ^= T7[buf[b]]; + res[b] = r; + } + memcpy(buf, res, 64); + } - GOST3411Block E (const GOST3411Block& m) - { - GOST3411Block k = *this; - GOST3411Block res = k^m; - for (int i = 0; i < 12; i++) - { - res.F (); - k = k^C_[i]; - k.F (); - res = k^res; - } - return res; - } - }; + GOST3411Block E(const GOST3411Block &m) { + GOST3411Block k = *this; + GOST3411Block res = k ^ m; + for (int i = 0; i < 12; i++) { + res.F(); + k = k ^ C_[i]; + k.F(); + res = k ^ res; + } + return res; + } + }; - static GOST3411Block gN (const GOST3411Block& N, const GOST3411Block& h, const GOST3411Block& m) - { - GOST3411Block res = N ^ h; - res.F (); - res = res.E (m); - res = res^h; - res = res^m; - return res; - } + static GOST3411Block gN(const GOST3411Block &N, const GOST3411Block &h, const GOST3411Block &m) { + GOST3411Block res = N ^ h; + res.F(); + res = res.E(m); + res = res ^ h; + res = res ^ m; + return res; + } - static void H (const uint8_t * iv, const uint8_t * buf, size_t len, uint8_t * digest) - { - // stage 1 - GOST3411Block h, N, s, m; - memcpy (h.buf, iv, 64); - memset (N.buf, 0, 64); - memset (s.buf, 0, 64); - size_t l = len; - // stage 2 - while (l >= 64) - { - memcpy (m.buf, buf + l - 64, 64); // TODO - h= gN (N, h, m); - N.Add (512); - s = m + s; - l -= 64; - } - // stage 3 - size_t padding = 64 - l; - if (padding) - { - memset (m.buf, 0, padding - 1); - m.buf[padding - 1] = 1; - } - memcpy (m.buf + padding, buf, l); + static void H(const uint8_t *iv, const uint8_t *buf, size_t len, uint8_t *digest) { + // stage 1 + GOST3411Block h, N, s, m; + memcpy(h.buf, iv, 64); + memset(N.buf, 0, 64); + memset(s.buf, 0, 64); + size_t l = len; + // stage 2 + while (l >= 64) { + memcpy(m.buf, buf + l - 64, 64); // TODO + h = gN(N, h, m); + N.Add(512); + s = m + s; + l -= 64; + } + // stage 3 + size_t padding = 64 - l; + if (padding) { + memset(m.buf, 0, padding - 1); + m.buf[padding - 1] = 1; + } + memcpy(m.buf + padding, buf, l); - h = gN (N, h, m); - N.Add (l*8); - s = m + s; + h = gN(N, h, m); + N.Add(l * 8); + s = m + s; - GOST3411Block N0; - memset (N0.buf, 0, 64); - h = gN (N0, h, N); - h = gN (N0, h, s); + GOST3411Block N0; + memset(N0.buf, 0, 64); + h = gN(N0, h, N); + h = gN(N0, h, s); - memcpy (digest, h.buf, 64); - } + memcpy(digest, h.buf, 64); + } - void GOSTR3411_2012_256 (const uint8_t * buf, size_t len, uint8_t * digest) - { - uint8_t iv[64]; - memset (iv, 1, 64); - uint8_t h[64]; - H (iv, buf, len, h); - memcpy (digest, h, 32); // first half - } + void GOSTR3411_2012_256(const uint8_t *buf, size_t len, uint8_t *digest) { + uint8_t iv[64]; + memset(iv, 1, 64); + uint8_t h[64]; + H(iv, buf, len, h); + memcpy(digest, h, 32); // first half + } - void GOSTR3411_2012_512 (const uint8_t * buf, size_t len, uint8_t * digest) - { - uint8_t iv[64]; - memset (iv, 0, 64); - H (iv, buf, len, digest); - } + void GOSTR3411_2012_512(const uint8_t *buf, size_t len, uint8_t *digest) { + uint8_t iv[64]; + memset(iv, 0, 64); + H(iv, buf, len, digest); + } - // reverse order - struct GOSTR3411_2012_CTX - { - GOST3411Block h, N, s, m; - size_t len; - bool is512; - }; + // reverse order + struct GOSTR3411_2012_CTX { + GOST3411Block h, N, s, m; + size_t len; + bool is512; + }; - GOSTR3411_2012_CTX * GOSTR3411_2012_CTX_new () - { - return new GOSTR3411_2012_CTX; - } + GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new() { + return new GOSTR3411_2012_CTX; + } - void GOSTR3411_2012_CTX_free (GOSTR3411_2012_CTX * ctx) - { - delete ctx; - } + void GOSTR3411_2012_CTX_free(GOSTR3411_2012_CTX *ctx) { + delete ctx; + } - void GOSTR3411_2012_CTX_Init (GOSTR3411_2012_CTX * ctx, bool is512) - { - uint8_t iv[64]; - memset (iv, is512 ? 0 : 1, 64); - memcpy (ctx->h.buf, iv, 64); - memset (ctx->N.buf, 0, 64); - memset (ctx->s.buf, 0, 64); - ctx->len = 0; - ctx->is512 = is512; - } + void GOSTR3411_2012_CTX_Init(GOSTR3411_2012_CTX *ctx, bool is512) { + uint8_t iv[64]; + memset(iv, is512 ? 0 : 1, 64); + memcpy(ctx->h.buf, iv, 64); + memset(ctx->N.buf, 0, 64); + memset(ctx->s.buf, 0, 64); + ctx->len = 0; + ctx->is512 = is512; + } - void GOSTR3411_2012_CTX_Update (const uint8_t * buf, size_t len, GOSTR3411_2012_CTX * ctx) - { - if (!len) return; - if (ctx->len > 0) // something left from buffer - { - size_t l = 64 - ctx->len; - if (len < l) l = len; - for (size_t i = 0; i < l; i++) - ctx->m.buf[ctx->len + i] = buf[l-i-1]; // invert - ctx->len += l; len -= l; buf += l; + void GOSTR3411_2012_CTX_Update(const uint8_t *buf, size_t len, GOSTR3411_2012_CTX *ctx) { + if (!len) return; + if (ctx->len > 0) // something left from buffer + { + size_t l = 64 - ctx->len; + if (len < l) l = len; + for (size_t i = 0; i < l; i++) + ctx->m.buf[ctx->len + i] = buf[l - i - 1]; // invert + ctx->len += l; + len -= l; + buf += l; - ctx->h = gN (ctx->N, ctx->h, ctx->m); - ctx->N.Add (512); - ctx->s = ctx->m + ctx->s; - } - while (len >= 64) - { - for (size_t i = 0; i < 64; i++) - ctx->m.buf[i] = buf[63-i]; // invert - len -= 64; buf += 64; - ctx->h = gN (ctx->N, ctx->h, ctx->m); - ctx->N.Add (512); - ctx->s = ctx->m + ctx->s; - } - if (len > 0) // carry remaining - { - for (size_t i = 0; i < len; i++) - ctx->m.buf[i] = buf[len-i-1]; // invert - } - ctx->len = len; - } + ctx->h = gN(ctx->N, ctx->h, ctx->m); + ctx->N.Add(512); + ctx->s = ctx->m + ctx->s; + } + while (len >= 64) { + for (size_t i = 0; i < 64; i++) + ctx->m.buf[i] = buf[63 - i]; // invert + len -= 64; + buf += 64; + ctx->h = gN(ctx->N, ctx->h, ctx->m); + ctx->N.Add(512); + ctx->s = ctx->m + ctx->s; + } + if (len > 0) // carry remaining + { + for (size_t i = 0; i < len; i++) + ctx->m.buf[i] = buf[len - i - 1]; // invert + } + ctx->len = len; + } - void GOSTR3411_2012_CTX_Finish (uint8_t * digest, GOSTR3411_2012_CTX * ctx) - { - GOST3411Block m; - size_t padding = 64 - ctx->len; - if (padding) - { - memset (m.buf, 0, padding - 1); - m.buf[padding - 1] = 1; - } - memcpy (m.buf + padding, ctx->m.buf, ctx->len); + void GOSTR3411_2012_CTX_Finish(uint8_t *digest, GOSTR3411_2012_CTX *ctx) { + GOST3411Block m; + size_t padding = 64 - ctx->len; + if (padding) { + memset(m.buf, 0, padding - 1); + m.buf[padding - 1] = 1; + } + memcpy(m.buf + padding, ctx->m.buf, ctx->len); - ctx->h = gN (ctx->N, ctx->h, m); - ctx->N.Add (ctx->len*8); - ctx->s = m + ctx->s; + ctx->h = gN(ctx->N, ctx->h, m); + ctx->N.Add(ctx->len * 8); + ctx->s = m + ctx->s; - GOST3411Block N0; - memset (N0.buf, 0, 64); - ctx->h = gN (N0, ctx->h, ctx->N); - ctx->h = gN (N0, ctx->h, ctx->s); + GOST3411Block N0; + memset(N0.buf, 0, 64); + ctx->h = gN(N0, ctx->h, ctx->N); + ctx->h = gN(N0, ctx->h, ctx->s); - size_t sz = ctx->is512 ? 64 : 32; - for (size_t i = 0; i < sz; i++) - digest[i] = ctx->h.buf[sz - i - 1]; - } + size_t sz = ctx->is512 ? 64 : 32; + for (size_t i = 0; i < sz; i++) + digest[i] = ctx->h.buf[sz - i - 1]; + } -} + } } diff --git a/libi2pd/Gost.h b/libi2pd/Gost.h index 0a79c30b..fc3d8994 100644 --- a/libi2pd/Gost.h +++ b/libi2pd/Gost.h @@ -12,59 +12,70 @@ #include #include -namespace i2p -{ -namespace crypto -{ +namespace i2p { + namespace crypto { - // ГОСТ Р 34.10 + // ГОСТ Р 34.10 - enum GOSTR3410ParamSet - { - eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1 - // XchA = A, XchB = C - //eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0 - //eGOSTR3410CryptoProXchB, // 1.2.643.2.2.36.1 - eGOSTR3410TC26A512, // 1.2.643.7.1.2.1.2.1 - eGOSTR3410NumParamSets - }; + enum GOSTR3410ParamSet { + eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1 + // XchA = A, XchB = C + //eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0 + //eGOSTR3410CryptoProXchB, // 1.2.643.2.2.36.1 + eGOSTR3410TC26A512, // 1.2.643.7.1.2.1.2.1 + eGOSTR3410NumParamSets + }; - class GOSTR3410Curve - { - public: + class GOSTR3410Curve { + public: - GOSTR3410Curve (BIGNUM * a, BIGNUM * b, BIGNUM * p, BIGNUM * q, BIGNUM * x, BIGNUM * y); - ~GOSTR3410Curve (); + GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y); - size_t GetKeyLen () const { return m_KeyLen; }; - 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; + ~GOSTR3410Curve(); - private: + size_t GetKeyLen() const { return m_KeyLen; }; - EC_GROUP * m_Group; - size_t m_KeyLen; // in bytes - }; + const EC_GROUP *GetGroup() const { return m_Group; }; - std::unique_ptr& 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 &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet); // Big Endian - 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_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); // Little Endian - struct GOSTR3411_2012_CTX; - GOSTR3411_2012_CTX * GOSTR3411_2012_CTX_new (); - void GOSTR3411_2012_CTX_Init (GOSTR3411_2012_CTX * ctx, bool is512 = true); - 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); -} + struct GOSTR3411_2012_CTX; + + GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new(); + + void GOSTR3411_2012_CTX_Init(GOSTR3411_2012_CTX *ctx, bool is512 = true); + + 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 diff --git a/libi2pd/Gzip.cpp b/libi2pd/Gzip.cpp index 07c6a96e..76d30a9e 100644 --- a/libi2pd/Gzip.cpp +++ b/libi2pd/Gzip.cpp @@ -13,189 +13,166 @@ #include "I2PEndian.h" #include "Gzip.h" -namespace i2p -{ -namespace data -{ - const size_t GZIP_CHUNK_SIZE = 16384; +namespace i2p { + namespace data { + const size_t GZIP_CHUNK_SIZE = 16384; - GzipInflator::GzipInflator (): m_IsDirty (false) - { - memset (&m_Inflator, 0, sizeof (m_Inflator)); - inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip - } + GzipInflator::GzipInflator() : m_IsDirty(false) { + memset(&m_Inflator, 0, sizeof(m_Inflator)); + inflateInit2(&m_Inflator, MAX_WBITS + 16); // gzip + } - GzipInflator::~GzipInflator () - { - inflateEnd (&m_Inflator); - } + GzipInflator::~GzipInflator() { + inflateEnd(&m_Inflator); + } - size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) - { - if (inLen < 23) return 0; - if (in[10] == 0x01) // non compressed - { - size_t len = bufle16toh (in + 11); - if (len + 23 < inLen) - { - LogPrint (eLogError, "Gzip: Incorrect length"); - return 0; - } - if (len > outLen) len = outLen; - memcpy (out, in + 15, len); - return len; - } - else - { - if (m_IsDirty) inflateReset (&m_Inflator); - m_IsDirty = true; - m_Inflator.next_in = const_cast(in); - m_Inflator.avail_in = inLen; - m_Inflator.next_out = out; - m_Inflator.avail_out = outLen; - int err; - if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) - return outLen - m_Inflator.avail_out; - // else - LogPrint (eLogError, "Gzip: Inflate error ", err); - return 0; - } - } + size_t GzipInflator::Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen) { + if (inLen < 23) return 0; + if (in[10] == 0x01) // non compressed + { + size_t len = bufle16toh(in + 11); + if (len + 23 < inLen) { + LogPrint(eLogError, "Gzip: Incorrect length"); + return 0; + } + if (len > outLen) len = outLen; + memcpy(out, in + 15, len); + return len; + } else { + if (m_IsDirty) inflateReset(&m_Inflator); + m_IsDirty = true; + m_Inflator.next_in = const_cast(in); + m_Inflator.avail_in = inLen; + m_Inflator.next_out = out; + m_Inflator.avail_out = outLen; + int err; + if ((err = inflate(&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) + return outLen - m_Inflator.avail_out; + // else + LogPrint(eLogError, "Gzip: Inflate error ", err); + return 0; + } + } - void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os) - { - m_IsDirty = true; - uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE]; - m_Inflator.next_in = const_cast(in); - m_Inflator.avail_in = inLen; - int ret; - do - { - m_Inflator.next_out = out; - m_Inflator.avail_out = GZIP_CHUNK_SIZE; - ret = inflate (&m_Inflator, Z_NO_FLUSH); - if (ret < 0) - { - inflateEnd (&m_Inflator); - os.setstate(std::ios_base::failbit); - break; - } - 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(const uint8_t *in, size_t inLen, std::ostream &os) { + m_IsDirty = true; + uint8_t *out = new uint8_t[GZIP_CHUNK_SIZE]; + m_Inflator.next_in = const_cast(in); + m_Inflator.avail_in = inLen; + int ret; + do { + m_Inflator.next_out = out; + m_Inflator.avail_out = GZIP_CHUNK_SIZE; + ret = inflate(&m_Inflator, Z_NO_FLUSH); + if (ret < 0) { + inflateEnd(&m_Inflator); + os.setstate(std::ios_base::failbit); + break; + } + 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) - { - uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE]; - while (!in.eof ()) - { - in.read ((char *) buf, GZIP_CHUNK_SIZE); - Inflate (buf, in.gcount (), out); - } - delete[] buf; - } + void GzipInflator::Inflate(std::istream &in, std::ostream &out) { + uint8_t *buf = new uint8_t[GZIP_CHUNK_SIZE]; + while (!in.eof()) { + in.read((char *) buf, GZIP_CHUNK_SIZE); + Inflate(buf, in.gcount(), out); + } + delete[] buf; + } - GzipDeflator::GzipDeflator (): m_IsDirty (false) - { - memset (&m_Deflator, 0, sizeof (m_Deflator)); - deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip - } + GzipDeflator::GzipDeflator() : m_IsDirty(false) { + memset(&m_Deflator, 0, sizeof(m_Deflator)); + deflateInit2(&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, + Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip + } - GzipDeflator::~GzipDeflator () - { - deflateEnd (&m_Deflator); - } + GzipDeflator::~GzipDeflator() { + deflateEnd(&m_Deflator); + } - void GzipDeflator::SetCompressionLevel (int level) - { - deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY); - } + void GzipDeflator::SetCompressionLevel(int level) { + deflateParams(&m_Deflator, level, Z_DEFAULT_STRATEGY); + } - size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) - { - if (m_IsDirty) deflateReset (&m_Deflator); - m_IsDirty = true; - m_Deflator.next_in = const_cast(in); - m_Deflator.avail_in = inLen; - m_Deflator.next_out = out; - m_Deflator.avail_out = outLen; - int err; - if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) - { - out[9] = 0xff; // OS is always unknown - return outLen - m_Deflator.avail_out; - } - // else - LogPrint (eLogError, "Gzip: Deflate error ", err); - return 0; - } + size_t GzipDeflator::Deflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen) { + if (m_IsDirty) deflateReset(&m_Deflator); + m_IsDirty = true; + m_Deflator.next_in = const_cast(in); + m_Deflator.avail_in = inLen; + m_Deflator.next_out = out; + m_Deflator.avail_out = outLen; + int err; + if ((err = deflate(&m_Deflator, Z_FINISH)) == Z_STREAM_END) { + out[9] = 0xff; // OS is always unknown + return outLen - m_Deflator.avail_out; + } + // else + LogPrint(eLogError, "Gzip: Deflate error ", err); + return 0; + } - size_t GzipDeflator::Deflate (const std::vector >& bufs, uint8_t * out, size_t outLen) - { - if (m_IsDirty) deflateReset (&m_Deflator); - m_IsDirty = true; - size_t offset = 0; - int err; - for (const auto& it: bufs) - { - m_Deflator.next_in = const_cast(it.first); - m_Deflator.avail_in = it.second; - m_Deflator.next_out = out + offset; - m_Deflator.avail_out = outLen - offset; - auto flush = (it == bufs.back ()) ? Z_FINISH : Z_NO_FLUSH; - err = deflate (&m_Deflator, flush); - if (err) - { - if (flush && err == Z_STREAM_END) - { - out[9] = 0xff; // OS is always unknown - return outLen - m_Deflator.avail_out; - } - break; - } - offset = outLen - m_Deflator.avail_out; - } - // else - LogPrint (eLogError, "Gzip: Deflate error ", err); - return 0; - } + size_t GzipDeflator::Deflate(const std::vector > &bufs, uint8_t *out, + size_t outLen) { + if (m_IsDirty) deflateReset(&m_Deflator); + m_IsDirty = true; + size_t offset = 0; + int err; + for (const auto &it: bufs) { + m_Deflator.next_in = const_cast(it.first); + m_Deflator.avail_in = it.second; + m_Deflator.next_out = out + offset; + m_Deflator.avail_out = outLen - offset; + auto flush = (it == bufs.back()) ? Z_FINISH : Z_NO_FLUSH; + err = deflate(&m_Deflator, flush); + if (err) { + if (flush && err == Z_STREAM_END) { + out[9] = 0xff; // OS is always unknown + return outLen - m_Deflator.avail_out; + } + break; + } + offset = outLen - m_Deflator.avail_out; + } + // else + LogPrint(eLogError, "Gzip: Deflate error ", err); + return 0; + } - size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen) - { - static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 }; - if (outLen < (size_t)inLen + 23) return 0; - memcpy (out, gzipHeader, 11); - htole16buf (out + 11, inLen); - htole16buf (out + 13, 0xffff - inLen); - memcpy (out + 15, in, inLen); - htole32buf (out + inLen + 15, crc32 (0, in, inLen)); - htole32buf (out + inLen + 19, inLen); - return inLen + 23; - } + size_t GzipNoCompression(const uint8_t *in, uint16_t inLen, uint8_t *out, size_t outLen) { + static const uint8_t gzipHeader[11] = {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01}; + if (outLen < (size_t) inLen + 23) return 0; + memcpy(out, gzipHeader, 11); + htole16buf(out + 11, inLen); + htole16buf(out + 13, 0xffff - inLen); + memcpy(out + 15, in, inLen); + htole32buf(out + inLen + 15, crc32(0, in, inLen)); + htole32buf(out + inLen + 19, inLen); + return inLen + 23; + } - size_t GzipNoCompression (const std::vector >& bufs, uint8_t * out, size_t outLen) - { - static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 }; - memcpy (out, gzipHeader, 11); - uint32_t crc = 0; - size_t len = 0, len1; - for (const auto& it: bufs) - { - len1 = len; - len += it.second; - if (outLen < len + 23) return 0; - memcpy (out + 15 + len1, it.first, it.second); - crc = crc32 (crc, it.first, it.second); - } - if (len > 0xffff) return 0; - htole32buf (out + len + 15, crc); - htole32buf (out + len + 19, len); - htole16buf (out + 11, len); - htole16buf (out + 13, 0xffff - len); - return len + 23; - } + size_t + GzipNoCompression(const std::vector > &bufs, uint8_t *out, size_t outLen) { + static const uint8_t gzipHeader[11] = {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01}; + memcpy(out, gzipHeader, 11); + uint32_t crc = 0; + size_t len = 0, len1; + for (const auto &it: bufs) { + len1 = len; + len += it.second; + if (outLen < len + 23) return 0; + memcpy(out + 15 + len1, it.first, it.second); + crc = crc32(crc, it.first, it.second); + } + if (len > 0xffff) return 0; + htole32buf(out + len + 15, crc); + htole32buf(out + len + 19, len); + htole16buf(out + 11, len); + htole16buf(out + 13, 0xffff - len); + return len + 23; + } -} // data + } // data } // i2p diff --git a/libi2pd/Gzip.h b/libi2pd/Gzip.h index d0bb28ed..60dc35f3 100644 --- a/libi2pd/Gzip.h +++ b/libi2pd/Gzip.h @@ -12,48 +12,51 @@ #include #include -namespace i2p -{ -namespace data -{ - class GzipInflator - { - public: +namespace i2p { + namespace data { + class GzipInflator { + public: - GzipInflator (); - ~GzipInflator (); + GzipInflator(); - size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); - /** @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); + ~GzipInflator(); - private: + size_t Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen); - z_stream m_Inflator; - bool m_IsDirty; - }; + /** @note @a os failbit will be set in case of error */ + void Inflate(const uint8_t *in, size_t inLen, std::ostream &os); - class GzipDeflator - { - public: + void Inflate(std::istream &in, std::ostream &out); - GzipDeflator (); - ~GzipDeflator (); + private: - void SetCompressionLevel (int level); - size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); - size_t Deflate (const std::vector >& bufs, uint8_t * out, size_t outLen); + z_stream m_Inflator; + bool m_IsDirty; + }; - private: + class GzipDeflator { + public: - z_stream m_Deflator; - bool m_IsDirty; - }; + GzipDeflator(); - size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen); // for < 64K - size_t GzipNoCompression (const std::vector >& bufs, uint8_t * out, size_t outLen); // for total size < 64K -} // data + ~GzipDeflator(); + + void SetCompressionLevel(int level); + + size_t Deflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen); + + size_t Deflate(const std::vector > &bufs, uint8_t *out, size_t outLen); + + private: + + z_stream m_Deflator; + bool m_IsDirty; + }; + + size_t GzipNoCompression(const uint8_t *in, uint16_t inLen, uint8_t *out, size_t outLen); // for < 64K + size_t GzipNoCompression(const std::vector > &bufs, uint8_t *out, + size_t outLen); // for total size < 64K + } // data } // i2p #endif /* GZIP_H__ */ diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index e994b9b3..6a0ba210 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -14,514 +14,531 @@ #include "Base.h" #include "HTTP.h" -namespace i2p -{ -namespace http -{ - const std::vector HTTP_METHODS = { - "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods - "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323 - }; - const std::vector HTTP_VERSIONS = { - "HTTP/1.0", "HTTP/1.1" - }; - const std::vector weekdays = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - const std::vector months = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; +namespace i2p { + namespace http { + const std::vector HTTP_METHODS = { + "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods + "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", + "SEARCH" // WebDAV methods, for SEARCH see rfc5323 + }; + const std::vector HTTP_VERSIONS = { + "HTTP/1.0", "HTTP/1.1" + }; + const std::vector weekdays = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + const std::vector months = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; - inline bool is_http_version(const std::string & str) { - return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS); - } + inline bool is_http_version(const std::string &str) { + return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS); + } - inline bool is_http_method(const std::string & str) { - return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS); - } + inline bool is_http_method(const std::string &str) { + return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS); + } - void strsplit(const std::string & line, std::vector &tokens, char delim, std::size_t limit = 0) { - std::size_t count = 0; - std::stringstream ss(line); - std::string token; - while (1) { - count++; - if (limit > 0 && count >= limit) - delim = '\n'; /* reset delimiter */ - if (!std::getline(ss, token, delim)) - break; - tokens.push_back(token); - } - } + void strsplit(const std::string &line, std::vector &tokens, char delim, std::size_t limit = 0) { + std::size_t count = 0; + std::stringstream ss(line); + std::string token; + while (1) { + count++; + if (limit > 0 && count >= limit) + delim = '\n'; /* reset delimiter */ + if (!std::getline(ss, token, delim)) + break; + tokens.push_back(token); + } + } - static std::pair parse_header_line(const std::string& line) - { - std::size_t pos = 0; - std::size_t len = 1; /*: */ - std::size_t max = line.length(); - if ((pos = line.find(':', pos)) == std::string::npos) - return std::make_pair("", ""); // no ':' found - if (pos + 1 < max) // ':' at the end of header is valid - { - while ((pos + len) < max && isspace(line.at(pos + len))) - len++; - if (len == 1) - return std::make_pair("", ""); // no following space, but something else - } - return std::make_pair(line.substr(0, pos), line.substr(pos + len)); - } + static std::pair parse_header_line(const std::string &line) { + std::size_t pos = 0; + std::size_t len = 1; /*: */ + std::size_t max = line.length(); + if ((pos = line.find(':', pos)) == std::string::npos) + return std::make_pair("", ""); // no ':' found + if (pos + 1 < max) // ':' at the end of header is valid + { + while ((pos + len) < max && isspace(line.at(pos + len))) + len++; + if (len == 1) + return std::make_pair("", ""); // no following space, but something else + } + return std::make_pair(line.substr(0, pos), line.substr(pos + len)); + } - void gen_rfc7231_date(std::string & out) { - std::time_t now = std::time(nullptr); - char buf[128]; - std::tm *tm = std::gmtime(&now); - snprintf(buf, sizeof(buf), "%s, %02d %s %d %02d:%02d:%02d GMT", - weekdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], - tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec - ); - out = buf; - } + void gen_rfc7231_date(std::string &out) { + std::time_t now = std::time(nullptr); + char buf[128]; + std::tm *tm = std::gmtime(&now); + snprintf(buf, sizeof(buf), "%s, %02d %s %d %02d:%02d:%02d GMT", + weekdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], + tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec + ); + out = buf; + } - bool URL::parse(const char *str, std::size_t len) { - std::string url(str, len ? len : strlen(str)); - return parse(url); - } + bool URL::parse(const char *str, std::size_t len) { + std::string url(str, len ? len : strlen(str)); + return parse(url); + } - bool URL::parse(const std::string& url) { - std::size_t pos_p = 0; /* < current parse position */ - std::size_t pos_c = 0; /* < work position */ - if(url.at(0) != '/' || pos_p > 0) { - std::size_t pos_s = 0; - /* schema */ - pos_c = url.find("://"); - if (pos_c != std::string::npos) { - schema = url.substr(0, pos_c); - pos_p = pos_c + 3; - } - /* user[:pass] */ - pos_s = url.find('/', pos_p); /* find first slash */ - pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */ - if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) { - std::size_t delim = url.find(':', pos_p); - if (delim && delim != std::string::npos && delim < pos_c) { - user = url.substr(pos_p, delim - pos_p); - delim += 1; - pass = url.substr(delim, pos_c - delim); - } else if(delim) { - user = url.substr(pos_p, pos_c - pos_p); - } - pos_p = pos_c + 1; - } - /* hostname[:port][/path] */ - if (url[pos_p] == '[') // ipv6 - { - auto pos_b = url.find(']', pos_p); - if (pos_b == std::string::npos) return false; - pos_c = url.find_first_of(":/", pos_b); - } - else - pos_c = url.find_first_of(":/", pos_p); - if (pos_c == std::string::npos) { - /* only hostname, without post and path */ - host = url.substr(pos_p, std::string::npos); - return true; - } else if (url.at(pos_c) == ':') { - host = url.substr(pos_p, pos_c - pos_p); - /* port[/path] */ - pos_p = pos_c + 1; - pos_c = url.find('/', pos_p); - std::string port_str = (pos_c == std::string::npos) - ? url.substr(pos_p, std::string::npos) - : url.substr(pos_p, pos_c - pos_p); - /* stoi throws exception on failure, we don't need it */ - for (char c : port_str) { - if (c < '0' || c > '9') - return false; - port *= 10; - port += c - '0'; - } - if (pos_c == std::string::npos) - return true; /* no path part */ - pos_p = pos_c; - } else { - /* start of path part found */ - host = url.substr(pos_p, pos_c - pos_p); - pos_p = pos_c; - } - } + bool URL::parse(const std::string &url) { + std::size_t pos_p = 0; /* < current parse position */ + std::size_t pos_c = 0; /* < work position */ + if (url.at(0) != '/' || pos_p > 0) { + std::size_t pos_s = 0; + /* schema */ + pos_c = url.find("://"); + if (pos_c != std::string::npos) { + schema = url.substr(0, pos_c); + pos_p = pos_c + 3; + } + /* user[:pass] */ + pos_s = url.find('/', pos_p); /* find first slash */ + pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */ + if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) { + std::size_t delim = url.find(':', pos_p); + if (delim && delim != std::string::npos && delim < pos_c) { + user = url.substr(pos_p, delim - pos_p); + delim += 1; + pass = url.substr(delim, pos_c - delim); + } else if (delim) { + user = url.substr(pos_p, pos_c - pos_p); + } + pos_p = pos_c + 1; + } + /* hostname[:port][/path] */ + if (url[pos_p] == '[') // ipv6 + { + auto pos_b = url.find(']', pos_p); + if (pos_b == std::string::npos) return false; + pos_c = url.find_first_of(":/", pos_b); + } else + pos_c = url.find_first_of(":/", pos_p); + if (pos_c == std::string::npos) { + /* only hostname, without post and path */ + host = url.substr(pos_p, std::string::npos); + return true; + } else if (url.at(pos_c) == ':') { + host = url.substr(pos_p, pos_c - pos_p); + /* port[/path] */ + pos_p = pos_c + 1; + pos_c = url.find('/', pos_p); + std::string port_str = (pos_c == std::string::npos) + ? url.substr(pos_p, std::string::npos) + : url.substr(pos_p, pos_c - pos_p); + /* stoi throws exception on failure, we don't need it */ + for (char c: port_str) { + if (c < '0' || c > '9') + return false; + port *= 10; + port += c - '0'; + } + if (pos_c == std::string::npos) + return true; /* no path part */ + pos_p = pos_c; + } else { + /* start of path part found */ + host = url.substr(pos_p, pos_c - pos_p); + pos_p = pos_c; + } + } - /* pos_p now at start of path part */ - pos_c = url.find_first_of("?#", pos_p); - if (pos_c == std::string::npos) { - /* only path, without fragment and query */ - path = url.substr(pos_p, std::string::npos); - return true; - } else if (url.at(pos_c) == '?') { - /* found query part */ - path = url.substr(pos_p, pos_c - pos_p); - pos_p = pos_c + 1; - pos_c = url.find('#', pos_p); - if (pos_c == std::string::npos) { - /* no fragment */ - query = url.substr(pos_p, std::string::npos); - return true; - } else { - query = url.substr(pos_p, pos_c - pos_p); - pos_p = pos_c + 1; - } - } else { - /* found fragment part */ - path = url.substr(pos_p, pos_c - pos_p); - pos_p = pos_c + 1; - } + /* pos_p now at start of path part */ + pos_c = url.find_first_of("?#", pos_p); + if (pos_c == std::string::npos) { + /* only path, without fragment and query */ + path = url.substr(pos_p, std::string::npos); + return true; + } else if (url.at(pos_c) == '?') { + /* found query part */ + path = url.substr(pos_p, pos_c - pos_p); + pos_p = pos_c + 1; + pos_c = url.find('#', pos_p); + if (pos_c == std::string::npos) { + /* no fragment */ + query = url.substr(pos_p, std::string::npos); + return true; + } else { + query = url.substr(pos_p, pos_c - pos_p); + pos_p = pos_c + 1; + } + } else { + /* found fragment part */ + path = url.substr(pos_p, pos_c - pos_p); + pos_p = pos_c + 1; + } - /* pos_p now at start of fragment part */ - frag = url.substr(pos_p, std::string::npos); - return true; - } + /* pos_p now at start of fragment part */ + frag = url.substr(pos_p, std::string::npos); + return true; + } - bool URL::parse_query(std::map & params) { - std::vector tokens; - strsplit(query, tokens, '&'); + bool URL::parse_query(std::map ¶ms) { + std::vector tokens; + strsplit(query, tokens, '&'); - params.clear(); - for (const auto& it : tokens) { - if (!it.length()) // empty - continue; - std::size_t eq = it.find ('='); - if (eq != std::string::npos) { - auto e = std::pair(it.substr(0, eq), it.substr(eq + 1)); - params.insert(e); - } else { - auto e = std::pair(it, ""); - params.insert(e); - } - } - return true; - } + params.clear(); + for (const auto &it: tokens) { + if (!it.length()) // empty + continue; + std::size_t eq = it.find('='); + if (eq != std::string::npos) { + auto e = std::pair(it.substr(0, eq), it.substr(eq + 1)); + params.insert(e); + } else { + auto e = std::pair(it, ""); + params.insert(e); + } + } + return true; + } - std::string URL::to_string() { - std::string out = ""; - if (schema != "") { - out = schema + "://"; - if (user != "" && pass != "") { - out += user + ":" + pass + "@"; - } else if (user != "") { - out += user + "@"; - } - if (port) { - out += host + ":" + std::to_string(port); - } else { - out += host; - } - } - out += path; - if (query != "") - out += "?" + query; - if (frag != "") - out += "#" + frag; - return out; - } + std::string URL::to_string() { + std::string out = ""; + if (schema != "") { + out = schema + "://"; + if (user != "" && pass != "") { + out += user + ":" + pass + "@"; + } else if (user != "") { + out += user + "@"; + } + if (port) { + out += host + ":" + std::to_string(port); + } else { + out += host; + } + } + out += path; + if (query != "") + out += "?" + query; + if (frag != "") + out += "#" + frag; + return out; + } - bool URL::is_i2p() const - { - return host.rfind(".i2p") == ( host.size() - 4 ); - } + bool URL::is_i2p() const { + return host.rfind(".i2p") == (host.size() - 4); + } - void HTTPMsg::add_header(const char *name, std::string & value, bool replace) { - add_header(name, value.c_str(), replace); - } + void HTTPMsg::add_header(const char *name, std::string &value, bool replace) { + add_header(name, value.c_str(), replace); + } - void HTTPMsg::add_header(const char *name, const char *value, bool replace) { - std::size_t count = headers.count(name); - if (count && !replace) - return; - if (count) { - headers[name] = value; - return; - } - headers.insert(std::pair(name, value)); - } + void HTTPMsg::add_header(const char *name, const char *value, bool replace) { + std::size_t count = headers.count(name); + if (count && !replace) + return; + if (count) { + headers[name] = value; + return; + } + headers.insert(std::pair(name, value)); + } - void HTTPMsg::del_header(const char *name) { - headers.erase(name); - } + void HTTPMsg::del_header(const char *name) { + headers.erase(name); + } - int HTTPReq::parse(const char *buf, size_t len) { - std::string str(buf, len); - return parse(str); - } + int HTTPReq::parse(const char *buf, size_t len) { + std::string str(buf, len); + return parse(str); + } - int HTTPReq::parse(const std::string& str) { - enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE; - std::size_t eoh = str.find(HTTP_EOH); /* request head size */ - std::size_t eol = 0, pos = 0; - URL url; + int HTTPReq::parse(const std::string &str) { + enum { + REQ_LINE, HEADER_LINE + } expect = REQ_LINE; + std::size_t eoh = str.find(HTTP_EOH); /* request head size */ + std::size_t eol = 0, pos = 0; + URL url; - if (eoh == std::string::npos) - return 0; /* str not contains complete request */ + if (eoh == std::string::npos) + return 0; /* str not contains complete request */ - while ((eol = str.find(CRLF, pos)) != std::string::npos) { - if (expect == REQ_LINE) { - std::string line = str.substr(pos, eol - pos); - std::vector tokens; - strsplit(line, tokens, ' '); - if (tokens.size() != 3) - return -1; - if (!is_http_method(tokens[0])) - return -1; - if (!is_http_version(tokens[2])) - return -1; - if (!url.parse(tokens[1])) - return -1; - /* all ok */ - method = tokens[0]; - uri = tokens[1]; - version = tokens[2]; - expect = HEADER_LINE; - } - else - { - std::string line = str.substr(pos, eol - pos); - auto p = parse_header_line(line); - if (p.first.length () > 0) - headers.push_back (p); - else - return -1; - } - pos = eol + strlen(CRLF); - if (pos >= eoh) - break; - } - return eoh + strlen(HTTP_EOH); - } + while ((eol = str.find(CRLF, pos)) != std::string::npos) { + if (expect == REQ_LINE) { + std::string line = str.substr(pos, eol - pos); + std::vector tokens; + strsplit(line, tokens, ' '); + if (tokens.size() != 3) + return -1; + if (!is_http_method(tokens[0])) + return -1; + if (!is_http_version(tokens[2])) + return -1; + if (!url.parse(tokens[1])) + return -1; + /* all ok */ + method = tokens[0]; + uri = tokens[1]; + version = tokens[2]; + expect = HEADER_LINE; + } else { + std::string line = str.substr(pos, eol - pos); + auto p = parse_header_line(line); + if (p.first.length() > 0) + headers.push_back(p); + else + return -1; + } + pos = eol + strlen(CRLF); + if (pos >= eoh) + break; + } + return eoh + strlen(HTTP_EOH); + } - void HTTPReq::write(std::ostream & o) - { - o << method << " " << uri << " " << version << CRLF; - for (auto & h : headers) - o << h.first << ": " << h.second << CRLF; - o << CRLF; - } + void HTTPReq::write(std::ostream &o) { + o << method << " " << uri << " " << version << CRLF; + for (auto &h: headers) + o << h.first << ": " << h.second << CRLF; + o << CRLF; + } - std::string HTTPReq::to_string() - { - std::stringstream ss; - write(ss); - return ss.str(); - } + std::string HTTPReq::to_string() { + std::stringstream ss; + write(ss); + return ss.str(); + } - void HTTPReq::AddHeader (const std::string& name, const std::string& value) - { - headers.push_back (std::make_pair(name, value)); - } + void HTTPReq::AddHeader(const std::string &name, const std::string &value) { + headers.push_back(std::make_pair(name, value)); + } - void HTTPReq::UpdateHeader (const std::string& name, const std::string& value) - { - for (auto& it : headers) - if (it.first == name) - { - it.second = value; - break; - } - } + void HTTPReq::UpdateHeader(const std::string &name, const std::string &value) { + for (auto &it: headers) + if (it.first == name) { + it.second = value; + break; + } + } - void HTTPReq::RemoveHeader (const std::string& name, const std::string& exempt) - { - for (auto it = headers.begin (); it != headers.end ();) - { - if (!it->first.compare(0, name.length (), name) && it->first != exempt) - it = headers.erase (it); - else - it++; - } - } + void HTTPReq::RemoveHeader(const std::string &name, const std::string &exempt) { + for (auto it = headers.begin(); it != headers.end();) { + if (!it->first.compare(0, name.length(), name) && it->first != exempt) + it = headers.erase(it); + else + it++; + } + } - std::string HTTPReq::GetHeader (const std::string& name) const - { - for (auto& it : headers) - if (it.first == name) - return it.second; - return ""; - } + std::string HTTPReq::GetHeader(const std::string &name) const { + for (auto &it: headers) + if (it.first == name) + return it.second; + return ""; + } - bool HTTPRes::is_chunked() const - { - auto it = headers.find("Transfer-Encoding"); - if (it == headers.end()) - return false; - if (it->second.find("chunked") != std::string::npos) - return true; - return false; - } + bool HTTPRes::is_chunked() const { + auto it = headers.find("Transfer-Encoding"); + if (it == headers.end()) + return false; + if (it->second.find("chunked") != std::string::npos) + return true; + return false; + } - bool HTTPRes::is_gzipped(bool includingI2PGzip) const - { - auto it = headers.find("Content-Encoding"); - if (it == headers.end()) - return false; /* no header */ - if (it->second.find("gzip") != std::string::npos) - return true; /* gotcha! */ - if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos) - return true; - return false; - } + bool HTTPRes::is_gzipped(bool includingI2PGzip) const { + auto it = headers.find("Content-Encoding"); + if (it == headers.end()) + return false; /* no header */ + if (it->second.find("gzip") != std::string::npos) + return true; /* gotcha! */ + if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos) + return true; + return false; + } - long int HTTPMsg::content_length() const - { - unsigned long int length = 0; - auto it = headers.find("Content-Length"); - if (it == headers.end()) - return -1; - errno = 0; - length = std::strtoul(it->second.c_str(), (char **) NULL, 10); - if (errno != 0) - return -1; - return length; - } + long int HTTPMsg::content_length() const { + unsigned long int length = 0; + auto it = headers.find("Content-Length"); + if (it == headers.end()) + return -1; + errno = 0; + length = std::strtoul(it->second.c_str(), (char **) NULL, 10); + if (errno != 0) + return -1; + return length; + } - int HTTPRes::parse(const char *buf, size_t len) { - std::string str(buf, len); - return parse(str); - } + int HTTPRes::parse(const char *buf, size_t len) { + std::string str(buf, len); + return parse(str); + } - int HTTPRes::parse(const std::string& str) { - enum { RES_LINE, HEADER_LINE } expect = RES_LINE; - std::size_t eoh = str.find(HTTP_EOH); /* request head size */ - std::size_t eol = 0, pos = 0; + int HTTPRes::parse(const std::string &str) { + enum { + RES_LINE, HEADER_LINE + } expect = RES_LINE; + std::size_t eoh = str.find(HTTP_EOH); /* request head size */ + std::size_t eol = 0, pos = 0; - if (eoh == std::string::npos) - return 0; /* str not contains complete request */ + if (eoh == std::string::npos) + return 0; /* str not contains complete request */ - while ((eol = str.find(CRLF, pos)) != std::string::npos) { - if (expect == RES_LINE) { - std::string line = str.substr(pos, eol - pos); - std::vector tokens; - strsplit(line, tokens, ' ', 3); - if (tokens.size() != 3) - return -1; - if (!is_http_version(tokens[0])) - return -1; - code = atoi(tokens[1].c_str()); - if (code < 100 || code >= 600) - return -1; - /* all ok */ - version = tokens[0]; - status = tokens[2]; - expect = HEADER_LINE; - } else { - std::string line = str.substr(pos, eol - pos); - auto p = parse_header_line(line); - if (p.first.length () > 0) - headers.insert (p); - else - return -1; - } - pos = eol + strlen(CRLF); - if (pos >= eoh) - break; - } - return eoh + strlen(HTTP_EOH); - } + while ((eol = str.find(CRLF, pos)) != std::string::npos) { + if (expect == RES_LINE) { + std::string line = str.substr(pos, eol - pos); + std::vector tokens; + strsplit(line, tokens, ' ', 3); + if (tokens.size() != 3) + return -1; + if (!is_http_version(tokens[0])) + return -1; + code = atoi(tokens[1].c_str()); + if (code < 100 || code >= 600) + return -1; + /* all ok */ + version = tokens[0]; + status = tokens[2]; + expect = HEADER_LINE; + } else { + std::string line = str.substr(pos, eol - pos); + auto p = parse_header_line(line); + if (p.first.length() > 0) + headers.insert(p); + else + return -1; + } + pos = eol + strlen(CRLF); + if (pos >= eoh) + break; + } + return eoh + strlen(HTTP_EOH); + } - std::string HTTPRes::to_string() { - if (version == "HTTP/1.1" && headers.count("Date") == 0) { - std::string date; - gen_rfc7231_date(date); - add_header("Date", date.c_str()); - } - if (status == "OK" && code != 200) - status = HTTPCodeToStatus(code); // update - if (body.length() > 0 && headers.count("Content-Length") == 0) - add_header("Content-Length", std::to_string(body.length()).c_str()); - /* build response */ - std::stringstream ss; - ss << version << " " << code << " " << status << CRLF; - for (auto & h : headers) { - ss << h.first << ": " << h.second << CRLF; - } - ss << CRLF; - if (body.length() > 0) - ss << body; - return ss.str(); - } + std::string HTTPRes::to_string() { + if (version == "HTTP/1.1" && headers.count("Date") == 0) { + std::string date; + gen_rfc7231_date(date); + add_header("Date", date.c_str()); + } + if (status == "OK" && code != 200) + status = HTTPCodeToStatus(code); // update + if (body.length() > 0 && headers.count("Content-Length") == 0) + add_header("Content-Length", std::to_string(body.length()).c_str()); + /* build response */ + std::stringstream ss; + ss << version << " " << code << " " << status << CRLF; + for (auto &h: headers) { + ss << h.first << ": " << h.second << CRLF; + } + ss << CRLF; + if (body.length() > 0) + ss << body; + return ss.str(); + } - const char * HTTPCodeToStatus(int code) { - const char *ptr; - switch (code) { - case 105: ptr = "Name Not Resolved"; break; - /* success */ - case 200: ptr = "OK"; break; - case 206: ptr = "Partial Content"; break; - /* redirect */ - case 301: ptr = "Moved Permanently"; break; - case 302: ptr = "Found"; break; - case 304: ptr = "Not Modified"; break; - case 307: ptr = "Temporary Redirect"; break; - /* client error */ - case 400: ptr = "Bad Request"; break; - case 401: ptr = "Unauthorized"; break; - case 403: ptr = "Forbidden"; break; - case 404: ptr = "Not Found"; break; - case 407: ptr = "Proxy Authentication Required"; break; - case 408: ptr = "Request Timeout"; break; - /* server error */ - case 500: ptr = "Internal Server Error"; break; - case 502: ptr = "Bad Gateway"; break; - case 503: ptr = "Not Implemented"; break; - case 504: ptr = "Gateway Timeout"; break; - default: ptr = "Unknown Status"; break; - } - return ptr; - } + const char *HTTPCodeToStatus(int code) { + const char *ptr; + switch (code) { + case 105: + ptr = "Name Not Resolved"; + break; + /* success */ + case 200: + ptr = "OK"; + break; + case 206: + ptr = "Partial Content"; + break; + /* redirect */ + case 301: + ptr = "Moved Permanently"; + break; + case 302: + ptr = "Found"; + break; + case 304: + ptr = "Not Modified"; + break; + case 307: + ptr = "Temporary Redirect"; + break; + /* client error */ + case 400: + ptr = "Bad Request"; + break; + case 401: + ptr = "Unauthorized"; + break; + case 403: + ptr = "Forbidden"; + break; + case 404: + ptr = "Not Found"; + break; + case 407: + ptr = "Proxy Authentication Required"; + break; + case 408: + ptr = "Request Timeout"; + break; + /* server error */ + case 500: + ptr = "Internal Server Error"; + break; + case 502: + ptr = "Bad Gateway"; + break; + case 503: + ptr = "Not Implemented"; + break; + case 504: + ptr = "Gateway Timeout"; + break; + default: + ptr = "Unknown Status"; + break; + } + return ptr; + } - std::string UrlDecode(const std::string& data, bool allow_null) - { - std::string decoded(data); - size_t pos = 0; - while ((pos = decoded.find('%', pos)) != std::string::npos) - { - char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16); - if (c == '\0' && !allow_null) - { - pos += 3; - continue; - } - decoded.replace(pos, 3, 1, c); - pos++; - } - return decoded; - } + std::string UrlDecode(const std::string &data, bool allow_null) { + std::string decoded(data); + size_t pos = 0; + while ((pos = decoded.find('%', pos)) != std::string::npos) { + char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16); + if (c == '\0' && !allow_null) { + pos += 3; + continue; + } + decoded.replace(pos, 3, 1, c); + pos++; + } + return decoded; + } - bool MergeChunkedResponse (std::istream& in, std::ostream& out) - { - std::string hexLen; - while (!in.eof ()) - { - std::getline (in, hexLen); - errno = 0; - long int len = strtoul(hexLen.c_str(), (char **) NULL, 16); - if (errno != 0) - return false; /* conversion error */ - if (len == 0) - return true; /* end of stream */ - if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */ - return false; /* too large chunk */ - char * buf = new char[len]; - in.read (buf, len); - out.write (buf, len); - delete[] buf; - std::getline (in, hexLen); // read \r\n after chunk - } - return true; - } + bool MergeChunkedResponse(std::istream &in, std::ostream &out) { + std::string hexLen; + while (!in.eof()) { + std::getline(in, hexLen); + errno = 0; + long int len = strtoul(hexLen.c_str(), (char **) NULL, 16); + if (errno != 0) + return false; /* conversion error */ + if (len == 0) + return true; /* end of stream */ + if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */ + return false; /* too large chunk */ + char *buf = new char[len]; + in.read(buf, len); + out.write(buf, len); + delete[] buf; + std::getline(in, hexLen); // read \r\n after chunk + } + return true; + } - std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass) - { - if (user.empty () && pass.empty ()) return ""; - return "Basic " + i2p::data::ToBase64Standard (user + ":" + pass); - } + std::string CreateBasicAuthorizationString(const std::string &user, const std::string &pass) { + if (user.empty() && pass.empty()) return ""; + return "Basic " + i2p::data::ToBase64Standard(user + ":" + pass); + } -} // http + } // http } // i2p diff --git a/libi2pd/HTTP.h b/libi2pd/HTTP.h index 9445a01a..3f3b5648 100644 --- a/libi2pd/HTTP.h +++ b/libi2pd/HTTP.h @@ -16,160 +16,165 @@ #include #include -namespace i2p -{ -namespace http -{ - const char CRLF[] = "\r\n"; /**< HTTP line terminator */ - const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */ - extern const std::vector HTTP_METHODS; /**< list of valid HTTP methods */ - extern const std::vector HTTP_VERSIONS; /**< list of valid HTTP versions */ +namespace i2p { + namespace http { + const char CRLF[] = "\r\n"; /**< HTTP line terminator */ + const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */ + extern const std::vector HTTP_METHODS; /**< list of valid HTTP methods */ + extern const std::vector HTTP_VERSIONS; /**< list of valid HTTP versions */ - struct URL - { - std::string schema; - std::string user; - std::string pass; - std::string host; - unsigned short int port; - std::string path; - std::string query; - std::string frag; + struct URL { + std::string schema; + std::string user; + std::string pass; + std::string host; + unsigned short int port; + std::string path; + std::string query; + 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 - * @return true on success, false on invalid url - */ - bool parse (const char *str, std::size_t len = 0); - bool parse (const std::string& url); + /** + * @brief Tries to parse url from string + * @return true on success, false on invalid url + */ + bool parse(const char *str, std::size_t len = 0); - /** - * @brief Parse query part of url to key/value map - * @note Honestly, this should be implemented with std::multimap - */ - bool parse_query(std::map & params); + bool parse(const std::string &url); - /** - * @brief Serialize URL structure to url - * @note Returns relative url if schema if empty, absolute url otherwise - */ - std::string to_string (); + /** + * @brief Parse query part of url to key/value map + * @note Honestly, this should be implemented with std::multimap + */ + bool parse_query(std::map ¶ms); - /** - * @brief return true if the host is inside i2p - */ - bool is_i2p() const; - }; + /** + * @brief Serialize URL structure to url + * @note Returns relative url if schema if empty, absolute url otherwise + */ + std::string to_string(); - struct HTTPMsg - { - std::map headers; + /** + * @brief return true if the host is inside i2p + */ + bool is_i2p() const; + }; - void add_header(const char *name, std::string & value, bool replace = false); - void add_header(const char *name, const char *value, bool replace = false); - void del_header(const char *name); + struct HTTPMsg { + std::map headers; - /** @brief Returns declared message length or -1 if unknown */ - long int content_length() const; - }; + void add_header(const char *name, std::string &value, bool replace = false); - struct HTTPReq - { - std::list > headers; - std::string version; - std::string method; - std::string uri; + void add_header(const char *name, const char *value, bool replace = false); - HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {}; + void del_header(const char *name); - /** - * @brief Tries to parse HTTP request 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); - int parse(const std::string& buf); + /** @brief Returns declared message length or -1 if unknown */ + long int content_length() const; + }; - /** @brief Serialize HTTP request to string */ - std::string to_string(); - void write(std::ostream & o); + struct HTTPReq { + std::list > headers; + std::string version; + std::string method; + std::string uri; - void AddHeader (const std::string& name, const std::string& value); - 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; - }; + HTTPReq() : version("HTTP/1.0"), method("GET"), uri("/") {}; - struct HTTPRes : HTTPMsg { - std::string version; - std::string status; - unsigned short int code; - /** - * @brief Simplifies response generation - * - * 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; + /** + * @brief Tries to parse HTTP request 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); - HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {} + int parse(const std::string &buf); - /** - * @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); - int parse(const std::string& buf); + /** @brief Serialize HTTP request to string */ + std::string to_string(); - /** - * @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 write(std::ostream & o); + void AddHeader(const std::string &name, const std::string &value); - /** @brief Checks that response declared as chunked data */ - bool is_chunked() const ; + void UpdateHeader(const std::string &name, const std::string &value); - /** @brief Checks that response contains compressed data */ - bool is_gzipped(bool includingI2PGzip = true) const; - }; + 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, ""); }; - /** - * @brief returns HTTP status string by integer code - * @param code HTTP code [100, 599] - * @return Immutable string with status - */ - const char * HTTPCodeToStatus(int code); + std::string GetHeader(const std::string &name) const; + }; - /** - * @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); + struct HTTPRes : HTTPMsg { + std::string version; + std::string status; + unsigned short int code; + /** + * @brief Simplifies response generation + * + * 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; - /** - * @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); + HTTPRes() : version("HTTP/1.1"), status("OK"), code(200) {} - 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 #endif /* HTTP_H__ */ diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index e19e782d..c0cc0245 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -24,853 +24,753 @@ using namespace i2p::transport; -namespace i2p -{ - std::shared_ptr NewI2NPMessage () - { - return std::make_shared >(); - } +namespace i2p { + std::shared_ptr NewI2NPMessage() { + return std::make_shared >(); + } - std::shared_ptr NewI2NPShortMessage () - { - return std::make_shared >(); - } + std::shared_ptr NewI2NPShortMessage() { + return std::make_shared >(); + } - std::shared_ptr NewI2NPTunnelMessage (bool endpoint) - { - return i2p::tunnel::tunnels.NewI2NPTunnelMessage (endpoint); - } + std::shared_ptr NewI2NPTunnelMessage(bool endpoint) { + return i2p::tunnel::tunnels.NewI2NPTunnelMessage(endpoint); + } - std::shared_ptr NewI2NPMessage (size_t len) - { - return (len < I2NP_MAX_SHORT_MESSAGE_SIZE - I2NP_HEADER_SIZE - 2) ? NewI2NPShortMessage () : NewI2NPMessage (); - } + std::shared_ptr NewI2NPMessage(size_t len) { + return (len < I2NP_MAX_SHORT_MESSAGE_SIZE - I2NP_HEADER_SIZE - 2) ? NewI2NPShortMessage() : NewI2NPMessage(); + } - void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID, bool checksum) - { - SetTypeID (msgType); - if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4); - SetMsgID (replyMsgID); - SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT); - UpdateSize (); - if (checksum) UpdateChks (); - } + void I2NPMessage::FillI2NPMessageHeader(I2NPMessageType msgType, uint32_t replyMsgID, bool checksum) { + SetTypeID(msgType); + if (!replyMsgID) RAND_bytes((uint8_t * ) & replyMsgID, 4); + SetMsgID(replyMsgID); + SetExpiration(i2p::util::GetMillisecondsSinceEpoch() + I2NP_MESSAGE_EXPIRATION_TIMEOUT); + UpdateSize(); + if (checksum) UpdateChks(); + } - void I2NPMessage::RenewI2NPMessageHeader () - { - uint32_t msgID; - RAND_bytes ((uint8_t *)&msgID, 4); - SetMsgID (msgID); - SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT); - } + void I2NPMessage::RenewI2NPMessageHeader() { + uint32_t msgID; + RAND_bytes((uint8_t * ) & msgID, 4); + SetMsgID(msgID); + SetExpiration(i2p::util::GetMillisecondsSinceEpoch() + I2NP_MESSAGE_EXPIRATION_TIMEOUT); + } - bool I2NPMessage::IsExpired () const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - auto exp = GetExpiration (); - return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future - } + bool I2NPMessage::IsExpired() const { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + auto exp = GetExpiration(); + return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || + (ts < exp - 3 * I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future + } - std::shared_ptr CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) - { - auto msg = NewI2NPMessage (len); - if (msg->Concat (buf, len) < len) - LogPrint (eLogError, "I2NP: Message length ", len, " exceeds max length ", msg->maxLen); - msg->FillI2NPMessageHeader (msgType, replyMsgID); - return msg; - } + std::shared_ptr + CreateI2NPMessage(I2NPMessageType msgType, const uint8_t *buf, size_t len, uint32_t replyMsgID) { + auto msg = NewI2NPMessage(len); + if (msg->Concat(buf, len) < len) + LogPrint(eLogError, "I2NP: Message length ", len, " exceeds max length ", msg->maxLen); + msg->FillI2NPMessageHeader(msgType, replyMsgID); + return msg; + } - std::shared_ptr CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) - { - auto msg = NewI2NPMessage (); - if (msg->offset + len < msg->maxLen) - { - memcpy (msg->GetBuffer (), buf, len); - msg->len = msg->offset + len; - msg->from = from; - } - else - LogPrint (eLogError, "I2NP: Message length ", len, " exceeds max length"); - return msg; - } + std::shared_ptr + CreateI2NPMessage(const uint8_t *buf, size_t len, std::shared_ptr from) { + auto msg = NewI2NPMessage(); + if (msg->offset + len < msg->maxLen) { + memcpy(msg->GetBuffer(), buf, len); + msg->len = msg->offset + len; + msg->from = from; + } else + LogPrint(eLogError, "I2NP: Message length ", len, " exceeds max length"); + return msg; + } - std::shared_ptr CopyI2NPMessage (std::shared_ptr msg) - { - if (!msg) return nullptr; - auto newMsg = NewI2NPMessage (msg->len); - newMsg->offset = msg->offset; - *newMsg = *msg; - return newMsg; - } + std::shared_ptr CopyI2NPMessage(std::shared_ptr msg) { + if (!msg) return nullptr; + auto newMsg = NewI2NPMessage(msg->len); + newMsg->offset = msg->offset; + *newMsg = *msg; + return newMsg; + } - std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID) - { - auto m = NewI2NPShortMessage (); - uint8_t * buf = m->GetPayload (); - if (msgID) - { - htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); - htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ()); - } - else // for SSU establishment - { - RAND_bytes ((uint8_t *)&msgID, 4); - htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); - htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ()); - } - m->len += DELIVERY_STATUS_SIZE; - m->FillI2NPMessageHeader (eI2NPDeliveryStatus); - return m; - } + std::shared_ptr CreateDeliveryStatusMsg(uint32_t msgID) { + auto m = NewI2NPShortMessage(); + uint8_t *buf = m->GetPayload(); + if (msgID) { + htobe32buf(buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); + htobe64buf(buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch()); + } else // for SSU establishment + { + RAND_bytes((uint8_t * ) & msgID, 4); + htobe32buf(buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); + htobe64buf(buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID()); + } + m->len += DELIVERY_STATUS_SIZE; + m->FillI2NPMessageHeader(eI2NPDeliveryStatus); + return m; + } - std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, - uint32_t replyTunnelID, bool exploratory, std::set * excludedPeers) - { - auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage (); - uint8_t * buf = m->GetPayload (); - memcpy (buf, key, 32); // key - buf += 32; - memcpy (buf, from, 32); // from - buf += 32; - uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP; - if (replyTunnelID) - { - *buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag - htobe32buf (buf+1, replyTunnelID); - buf += 5; - } - else - { - *buf = flag; // flag - buf++; - } + std::shared_ptr CreateRouterInfoDatabaseLookupMsg(const uint8_t *key, const uint8_t *from, + uint32_t replyTunnelID, bool exploratory, + std::set *excludedPeers) { + auto m = excludedPeers ? NewI2NPMessage() : NewI2NPShortMessage(); + uint8_t *buf = m->GetPayload(); + memcpy(buf, key, 32); // key + buf += 32; + memcpy(buf, from, 32); // from + buf += 32; + uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP; + if (replyTunnelID) { + *buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag + htobe32buf(buf + 1, replyTunnelID); + buf += 5; + } else { + *buf = flag; // flag + buf++; + } - if (excludedPeers) - { - int cnt = excludedPeers->size (); - htobe16buf (buf, cnt); - buf += 2; - for (auto& it: *excludedPeers) - { - memcpy (buf, it, 32); - buf += 32; - } - } - else - { - // nothing to exclude - htobuf16 (buf, 0); - buf += 2; - } + if (excludedPeers) { + int cnt = excludedPeers->size(); + htobe16buf(buf, cnt); + buf += 2; + for (auto &it: *excludedPeers) { + memcpy(buf, it, 32); + buf += 32; + } + } else { + // nothing to exclude + htobuf16(buf, 0); + buf += 2; + } - m->len += (buf - m->GetPayload ()); - m->FillI2NPMessageHeader (eI2NPDatabaseLookup); - return m; - } + m->len += (buf - m->GetPayload()); + m->FillI2NPMessageHeader(eI2NPDatabaseLookup); + return m; + } - std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, - const std::set& excludedFloodfills, - std::shared_ptr replyTunnel, const uint8_t * replyKey, - const uint8_t * replyTag, bool replyECIES) - { - int cnt = excludedFloodfills.size (); - auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage (); - uint8_t * buf = m->GetPayload (); - memcpy (buf, dest, 32); // key - buf += 32; - memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW - buf += 32; - *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags - *buf |= (replyECIES ? DATABASE_LOOKUP_ECIES_FLAG : DATABASE_LOOKUP_ENCRYPTION_FLAG); - buf ++; - htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID - buf += 4; + std::shared_ptr CreateLeaseSetDatabaseLookupMsg(const i2p::data::IdentHash &dest, + const std::set &excludedFloodfills, + std::shared_ptr replyTunnel, + const uint8_t *replyKey, + const uint8_t *replyTag, bool replyECIES) { + int cnt = excludedFloodfills.size(); + auto m = cnt > 7 ? NewI2NPMessage() : NewI2NPShortMessage(); + uint8_t *buf = m->GetPayload(); + memcpy(buf, dest, 32); // key + buf += 32; + memcpy(buf, replyTunnel->GetNextIdentHash(), 32); // reply tunnel GW + buf += 32; + *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags + *buf |= (replyECIES ? DATABASE_LOOKUP_ECIES_FLAG : DATABASE_LOOKUP_ENCRYPTION_FLAG); + buf++; + htobe32buf(buf, replyTunnel->GetNextTunnelID()); // reply tunnel ID + buf += 4; - // excluded - if (cnt > 512) - { - LogPrint (eLogWarning, "I2NP: Too many peers to exclude ", cnt, " for DatabaseLookup"); - cnt = 0; - } - htobe16buf (buf, cnt); - buf += 2; - if (cnt > 0) - { - for (auto& it: excludedFloodfills) - { - memcpy (buf, it, 32); - buf += 32; - } - } - // encryption - memcpy (buf, replyKey, 32); - buf[32] = 1; // 1 tag - if (replyECIES) - { - memcpy (buf + 33, replyTag, 8); // 8 bytes tag - buf += 41; - } - else - { - memcpy (buf + 33, replyTag, 32); // 32 bytes tag - buf += 65; - } + // excluded + if (cnt > 512) { + LogPrint(eLogWarning, "I2NP: Too many peers to exclude ", cnt, " for DatabaseLookup"); + cnt = 0; + } + htobe16buf(buf, cnt); + buf += 2; + if (cnt > 0) { + for (auto &it: excludedFloodfills) { + memcpy(buf, it, 32); + buf += 32; + } + } + // encryption + memcpy(buf, replyKey, 32); + buf[32] = 1; // 1 tag + if (replyECIES) { + memcpy(buf + 33, replyTag, 8); // 8 bytes tag + buf += 41; + } else { + memcpy(buf + 33, replyTag, 32); // 32 bytes tag + buf += 65; + } - m->len += (buf - m->GetPayload ()); - m->FillI2NPMessageHeader (eI2NPDatabaseLookup); - return m; - } + m->len += (buf - m->GetPayload()); + m->FillI2NPMessageHeader(eI2NPDatabaseLookup); + return m; + } - std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, - std::vector routers) - { - auto m = NewI2NPShortMessage (); - uint8_t * buf = m->GetPayload (); - size_t len = 0; - memcpy (buf, ident, 32); - len += 32; - buf[len] = routers.size (); - len++; - for (const auto& it: routers) - { - memcpy (buf + len, it, 32); - len += 32; - } - memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32); - len += 32; - m->len += len; - m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply); - return m; - } + std::shared_ptr CreateDatabaseSearchReply(const i2p::data::IdentHash &ident, + std::vector routers) { + auto m = NewI2NPShortMessage(); + uint8_t *buf = m->GetPayload(); + size_t len = 0; + memcpy(buf, ident, 32); + len += 32; + buf[len] = routers.size(); + len++; + for (const auto &it: routers) { + memcpy(buf + len, it, 32); + len += 32; + } + memcpy(buf + len, i2p::context.GetRouterInfo().GetIdentHash(), 32); + len += 32; + m->len += len; + m->FillI2NPMessageHeader(eI2NPDatabaseSearchReply); + return m; + } - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router, - uint32_t replyToken, std::shared_ptr replyTunnel) - { - if (!router) // we send own RouterInfo - router = context.GetSharedRouterInfo (); + std::shared_ptr CreateDatabaseStoreMsg(std::shared_ptr router, + uint32_t replyToken, + std::shared_ptr replyTunnel) { + if (!router) // we send own RouterInfo + router = context.GetSharedRouterInfo(); - if (!router->GetBuffer ()) - { - LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore"); - return nullptr; - } + if (!router->GetBuffer()) { + LogPrint(eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore"); + return nullptr; + } - auto m = NewI2NPShortMessage (); - uint8_t * payload = m->GetPayload (); + auto m = NewI2NPShortMessage(); + uint8_t *payload = m->GetPayload(); - memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); - payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); - uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - if (replyTunnel) - { - htobe32buf (buf, replyTunnel->GetNextTunnelID ()); - buf += 4; // reply tunnelID - memcpy (buf, replyTunnel->GetNextIdentHash (), 32); - buf += 32; // reply tunnel gateway - } - else - { - memset (buf, 0, 4); // zero tunnelID means direct reply - buf += 4; - memcpy (buf, context.GetIdentHash (), 32); - buf += 32; - } - } + memcpy(payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash(), 32); + payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo + htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); + uint8_t *buf = payload + DATABASE_STORE_HEADER_SIZE; + if (replyToken) { + if (replyTunnel) { + htobe32buf(buf, replyTunnel->GetNextTunnelID()); + buf += 4; // reply tunnelID + memcpy(buf, replyTunnel->GetNextIdentHash(), 32); + buf += 32; // reply tunnel gateway + } else { + memset(buf, 0, 4); // zero tunnelID means direct reply + buf += 4; + memcpy(buf, context.GetIdentHash(), 32); + buf += 32; + } + } - uint8_t * sizePtr = buf; - buf += 2; - m->len += (buf - payload); // payload size - size_t size = 0; - if (router->GetBufferLen () + (buf - payload) <= 940) // fits one tunnel message - size = i2p::data::GzipNoCompression (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); - else - { - i2p::data::GzipDeflator deflator; - size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); - } - if (size) - { - htobe16buf (sizePtr, size); // size - m->len += size; - } - else - m = nullptr; - if (m) - m->FillI2NPMessageHeader (eI2NPDatabaseStore); - return m; - } + uint8_t *sizePtr = buf; + buf += 2; + m->len += (buf - payload); // payload size + size_t size = 0; + if (router->GetBufferLen() + (buf - payload) <= 940) // fits one tunnel message + size = i2p::data::GzipNoCompression(router->GetBuffer(), router->GetBufferLen(), buf, m->maxLen - m->len); + else { + i2p::data::GzipDeflator deflator; + size = deflator.Deflate(router->GetBuffer(), router->GetBufferLen(), buf, m->maxLen - m->len); + } + if (size) { + htobe16buf(sizePtr, size); // size + m->len += size; + } else + m = nullptr; + if (m) + m->FillI2NPMessageHeader(eI2NPDatabaseStore); + return m; + } - std::shared_ptr CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr leaseSet) - { - if (!leaseSet) return nullptr; - auto m = NewI2NPShortMessage (); - uint8_t * payload = m->GetPayload (); - memcpy (payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32); - payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); - size_t size = DATABASE_STORE_HEADER_SIZE; - memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); - size += leaseSet->GetBufferLen (); - m->len += size; - m->FillI2NPMessageHeader (eI2NPDatabaseStore); - return m; - } + std::shared_ptr + CreateDatabaseStoreMsg(const i2p::data::IdentHash &storeHash, std::shared_ptr leaseSet) { + if (!leaseSet) return nullptr; + auto m = NewI2NPShortMessage(); + uint8_t *payload = m->GetPayload(); + memcpy(payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32); + payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType(); // 1 for LeaseSet + htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); + size_t size = DATABASE_STORE_HEADER_SIZE; + memcpy(payload + size, leaseSet->GetBuffer(), leaseSet->GetBufferLen()); + size += leaseSet->GetBufferLen(); + m->len += size; + m->FillI2NPMessageHeader(eI2NPDatabaseStore); + return m; + } - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken, std::shared_ptr replyTunnel) - { - if (!leaseSet) return nullptr; - auto m = NewI2NPShortMessage (); - uint8_t * payload = m->GetPayload (); - memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetStoreHash (), 32); - payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // LeaseSet or LeaseSet2 - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); - size_t size = DATABASE_STORE_HEADER_SIZE; - if (replyToken && replyTunnel) - { - if (replyTunnel) - { - htobe32buf (payload + size, replyTunnel->GetNextTunnelID ()); - size += 4; // reply tunnelID - memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32); - size += 32; // reply tunnel gateway - } - else - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); - } - memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); - size += leaseSet->GetBufferLen (); - m->len += size; - m->FillI2NPMessageHeader (eI2NPDatabaseStore); - return m; - } + std::shared_ptr + CreateDatabaseStoreMsg(std::shared_ptr leaseSet, uint32_t replyToken, + std::shared_ptr replyTunnel) { + if (!leaseSet) return nullptr; + auto m = NewI2NPShortMessage(); + uint8_t *payload = m->GetPayload(); + memcpy(payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetStoreHash(), 32); + payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType(); // LeaseSet or LeaseSet2 + htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); + size_t size = DATABASE_STORE_HEADER_SIZE; + if (replyToken && replyTunnel) { + if (replyTunnel) { + htobe32buf(payload + size, replyTunnel->GetNextTunnelID()); + size += 4; // reply tunnelID + memcpy(payload + size, replyTunnel->GetNextIdentHash(), 32); + size += 32; // reply tunnel gateway + } else + htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); + } + memcpy(payload + size, leaseSet->GetBuffer(), leaseSet->GetBufferLen()); + size += leaseSet->GetBufferLen(); + m->len += size; + m->FillI2NPMessageHeader(eI2NPDatabaseStore); + return m; + } - bool IsRouterInfoMsg (std::shared_ptr msg) - { - if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false; - return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo - } + bool IsRouterInfoMsg(std::shared_ptr msg) { + if (!msg || msg->GetTypeID() != eI2NPDatabaseStore) return false; + return !msg->GetPayload()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo + } - static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO: - void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels) - { - if (maxNumTransitTunnels > 0 && g_MaxNumTransitTunnels != maxNumTransitTunnels) - { - LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels); - g_MaxNumTransitTunnels = maxNumTransitTunnels; - } - } + static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO: + void SetMaxNumTransitTunnels(uint16_t maxNumTransitTunnels) { + if (maxNumTransitTunnels > 0 && g_MaxNumTransitTunnels != maxNumTransitTunnels) { + LogPrint(eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels); + g_MaxNumTransitTunnels = maxNumTransitTunnels; + } + } - uint16_t GetMaxNumTransitTunnels () - { - return g_MaxNumTransitTunnels; - } + uint16_t GetMaxNumTransitTunnels() { + return g_MaxNumTransitTunnels; + } - static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) - { - for (int i = 0; i < num; i++) - { - uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; - if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); - if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false; - uint8_t retCode = 0; - // replace record to reply - if (i2p::context.AcceptsTunnels () && - i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels && - !i2p::transport::transports.IsBandwidthExceeded () && - !i2p::transport::transports.IsTransitBandwidthExceeded ()) - { - auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); - i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - } - else - retCode = 30; // always reject with bandwidth reason (30) + static bool HandleBuildRequestRecords(int num, uint8_t *records, uint8_t *clearText) { + for (int i = 0; i < num; i++) { + uint8_t *record = records + i * TUNNEL_BUILD_RECORD_SIZE; + if (!memcmp(record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, + (const uint8_t *) i2p::context.GetRouterInfo().GetIdentHash(), 16)) { + LogPrint(eLogDebug, "I2NP: Build request record ", i, " is ours"); + if (!i2p::context.DecryptTunnelBuildRecord(record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, + clearText)) + return false; + uint8_t retCode = 0; + // replace record to reply + if (i2p::context.AcceptsTunnels() && + i2p::tunnel::tunnels.GetTransitTunnels().size() <= g_MaxNumTransitTunnels && + !i2p::transport::transports.IsBandwidthExceeded() && + !i2p::transport::transports.IsTransitBandwidthExceeded()) { + auto transitTunnel = i2p::tunnel::CreateTransitTunnel( + bufbe32toh(clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, + clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + i2p::tunnel::tunnels.AddTransitTunnel(transitTunnel); + } else + retCode = 30; // always reject with bandwidth reason (30) - memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; - // encrypt reply - i2p::crypto::CBCEncryption encryption; - for (int j = 0; j < num; j++) - { - uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - if (j == i) - { - uint8_t nonce[12]; - memset (nonce, 0, 12); - auto& noiseState = i2p::context.GetCurrentNoiseState (); - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); - return false; - } - } - else - { - encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); - } - } - return true; - } - } - return false; - } + memset(record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; + // encrypt reply + i2p::crypto::CBCEncryption encryption; + for (int j = 0; j < num; j++) { + uint8_t *reply = records + j * TUNNEL_BUILD_RECORD_SIZE; + if (j == i) { + uint8_t nonce[12]; + memset(nonce, 0, 12); + auto &noiseState = i2p::context.GetCurrentNoiseState(); + if (!i2p::crypto::AEADChaCha20Poly1305(reply, TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, noiseState.m_CK, nonce, reply, + TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint(eLogWarning, "I2NP: Reply AEAD encryption failed"); + return false; + } + } else { + encryption.SetKey(clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV(clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + } + } + return true; + } + } + return false; + } - static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) - { - int num = buf[0]; - LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); - if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len); - return; - } + static void HandleVariableTunnelBuildMsg(uint32_t replyMsgID, uint8_t *buf, size_t len) { + int num = buf[0]; + LogPrint(eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); + if (len < num * TUNNEL_BUILD_RECORD_SIZE + 1) { + LogPrint(eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len); + return; + } - auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (buf, len)) - { - LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); - i2p::tunnel::tunnels.AddInboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); - } - } - else - { - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) - { - if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - } - } + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel(replyMsgID); + if (tunnel) { + // endpoint of inbound tunnel + LogPrint(eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID()); + if (tunnel->HandleTunnelBuildResponse(buf, len)) { + LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been created"); + tunnel->SetState(i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel(tunnel); + } else { + LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been declined"); + tunnel->SetState(i2p::tunnel::eTunnelStateBuildFailed); + } + } else { + uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords(num, buf + 1, clearText)) { + if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & + TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel + { + // so we send it to reply tunnel + transports.SendMessage(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg(bufbe32toh( + clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPVariableTunnelBuildReply, buf, len, + bufbe32toh(clearText + + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } else + transports.SendMessage(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage(eI2NPVariableTunnelBuild, buf, len, + bufbe32toh(clearText + + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + } + } - static void HandleTunnelBuildMsg (uint8_t * buf, size_t len) - { - LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); - } + static void HandleTunnelBuildMsg(uint8_t *buf, size_t len) { + LogPrint(eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); + } - static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort) - { - int num = buf[0]; - LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); - size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; - if (len < num*recordSize + 1) - { - LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len); - return; - } + static void HandleTunnelBuildReplyMsg(uint32_t replyMsgID, uint8_t *buf, size_t len, bool isShort) { + int num = buf[0]; + LogPrint(eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); + size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + if (len < num * recordSize + 1) { + LogPrint(eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len); + return; + } - auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID); - if (tunnel) - { - // reply for outbound tunnel - if (tunnel->HandleTunnelBuildResponse (buf, len)) - { - LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); - i2p::tunnel::tunnels.AddOutboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); - } - } - else - LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); - } + auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel(replyMsgID); + if (tunnel) { + // reply for outbound tunnel + if (tunnel->HandleTunnelBuildResponse(buf, len)) { + LogPrint(eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID(), " has been created"); + tunnel->SetState(i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddOutboundTunnel(tunnel); + } else { + LogPrint(eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID(), " has been declined"); + tunnel->SetState(i2p::tunnel::eTunnelStateBuildFailed); + } + } else + LogPrint(eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); + } - static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) - { - int num = buf[0]; - LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); - if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); - return; - } - auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (buf, len)) - { - LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); - i2p::tunnel::tunnels.AddInboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); - } - return; - } - const uint8_t * record = buf + 1; - for (int i = 0; i < num; i++) - { - if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours"); - uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); - return; - } - if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES - { - LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); - return; - } - auto& noiseState = i2p::context.GetCurrentNoiseState (); - uint8_t replyKey[32], layerKey[32], ivKey[32]; - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); - memcpy (replyKey, noiseState.m_CK + 32, 32); - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); - memcpy (layerKey, noiseState.m_CK + 32, 32); - bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint) - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); - memcpy (ivKey, noiseState.m_CK + 32, 32); - } - else - memcpy (ivKey, noiseState.m_CK , 32); + static void HandleShortTunnelBuildMsg(uint32_t replyMsgID, uint8_t *buf, size_t len) { + int num = buf[0]; + LogPrint(eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); + if (len < num * SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) { + LogPrint(eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); + return; + } + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel(replyMsgID); + if (tunnel) { + // endpoint of inbound tunnel + LogPrint(eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID()); + if (tunnel->HandleTunnelBuildResponse(buf, len)) { + LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been created"); + tunnel->SetState(i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel(tunnel); + } else { + LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been declined"); + tunnel->SetState(i2p::tunnel::eTunnelStateBuildFailed); + } + return; + } + const uint8_t *record = buf + 1; + for (int i = 0; i < num; i++) { + if (!memcmp(record, (const uint8_t *) i2p::context.GetRouterInfo().GetIdentHash(), 16)) { + LogPrint(eLogDebug, "I2NP: Short request record ", i, " is ours"); + uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (!i2p::context.DecryptTunnelShortRequestRecord(record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, + clearText)) { + LogPrint(eLogWarning, "I2NP: Can't decrypt short request record ", i); + return; + } + if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES + { + LogPrint(eLogWarning, "I2NP: Unknown layer encryption type ", + clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); + return; + } + auto &noiseState = i2p::context.GetCurrentNoiseState(); + uint8_t replyKey[32], layerKey[32], ivKey[32]; + i2p::crypto::HKDF(noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); + memcpy(replyKey, noiseState.m_CK + 32, 32); + i2p::crypto::HKDF(noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); + memcpy(layerKey, noiseState.m_CK + 32, 32); + bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + if (isEndpoint) { + i2p::crypto::HKDF(noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); + memcpy(ivKey, noiseState.m_CK + 32, 32); + } else + memcpy(ivKey, noiseState.m_CK, 32); - // check if we accept this tunnel - uint8_t retCode = 0; - if (!i2p::context.AcceptsTunnels () || - i2p::tunnel::tunnels.GetTransitTunnels ().size () > g_MaxNumTransitTunnels || - i2p::transport::transports.IsBandwidthExceeded () || - i2p::transport::transports.IsTransitBandwidthExceeded ()) - retCode = 30; - if (!retCode) - { - // create new transit tunnel - auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - layerKey, ivKey, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); - i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - } + // check if we accept this tunnel + uint8_t retCode = 0; + if (!i2p::context.AcceptsTunnels() || + i2p::tunnel::tunnels.GetTransitTunnels().size() > g_MaxNumTransitTunnels || + i2p::transport::transports.IsBandwidthExceeded() || + i2p::transport::transports.IsTransitBandwidthExceeded()) + retCode = 30; + if (!retCode) { + // create new transit tunnel + auto transitTunnel = i2p::tunnel::CreateTransitTunnel( + bufbe32toh(clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh(clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + layerKey, ivKey, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + i2p::tunnel::tunnels.AddTransitTunnel(transitTunnel); + } - // encrypt reply - uint8_t nonce[12]; - memset (nonce, 0, 12); - uint8_t * reply = buf + 1; - for (int j = 0; j < num; j++) - { - nonce[4] = j; // nonce is record # - if (j == i) - { - memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); - return; - } - } - else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); - reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - // send reply - if (isEndpoint) - { - auto replyMsg = NewI2NPShortMessage (); - replyMsg->Concat (buf, len); - replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); - uint64_t tag; - memcpy (&tag, noiseState.m_CK, 8); - // we send it to reply tunnel - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); - } - else - { - // IBGW is local - uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); - auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); - if (tunnel) - tunnel->SendTunnelDataMsg (replyMsg); - else - LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); - } - } - else - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - return; - } - record += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - } + // encrypt reply + uint8_t nonce[12]; + memset(nonce, 0, 12); + uint8_t *reply = buf + 1; + for (int j = 0; j < num; j++) { + nonce[4] = j; // nonce is record # + if (j == i) { + memset(reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; + if (!i2p::crypto::AEADChaCha20Poly1305(reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, replyKey, nonce, reply, + SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint(eLogWarning, "I2NP: Short reply AEAD encryption failed"); + return; + } + } else + i2p::crypto::ChaCha20(reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); + reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + // send reply + if (isEndpoint) { + auto replyMsg = NewI2NPShortMessage(); + replyMsg->Concat(buf, len); + replyMsg->FillI2NPMessageHeader(eI2NPShortTunnelBuildReply, + bufbe32toh(clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (memcmp((const uint8_t *) i2p::context.GetIdentHash(), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? + { + i2p::crypto::HKDF(noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); + uint64_t tag; + memcpy(&tag, noiseState.m_CK, 8); + // we send it to reply tunnel + transports.SendMessage(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg( + bufbe32toh(clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + i2p::garlic::WrapECIESX25519Message(replyMsg, + noiseState.m_CK + 32, tag))); + } else { + // IBGW is local + uint32_t tunnelID = bufbe32toh(clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); + auto tunnel = i2p::tunnel::tunnels.GetTunnel(tunnelID); + if (tunnel) + tunnel->SendTunnelDataMsg(replyMsg); + else + LogPrint(eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); + } + } else + transports.SendMessage(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage(eI2NPShortTunnelBuild, buf, len, + bufbe32toh(clearText + + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + return; + } + record += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + } - std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf) - { - auto msg = NewI2NPTunnelMessage (false); - msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); - msg->FillI2NPMessageHeader (eI2NPTunnelData); - return msg; - } + std::shared_ptr CreateTunnelDataMsg(const uint8_t *buf) { + auto msg = NewI2NPTunnelMessage(false); + msg->Concat(buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); + msg->FillI2NPMessageHeader(eI2NPTunnelData); + return msg; + } - std::shared_ptr CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) - { - auto msg = NewI2NPTunnelMessage (false); - htobe32buf (msg->GetPayload (), tunnelID); - msg->len += 4; // tunnelID - msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); - msg->FillI2NPMessageHeader (eI2NPTunnelData); - return msg; - } + std::shared_ptr CreateTunnelDataMsg(uint32_t tunnelID, const uint8_t *payload) { + auto msg = NewI2NPTunnelMessage(false); + htobe32buf(msg->GetPayload(), tunnelID); + msg->len += 4; // tunnelID + msg->Concat(payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); + msg->FillI2NPMessageHeader(eI2NPTunnelData); + return msg; + } - std::shared_ptr CreateEmptyTunnelDataMsg (bool endpoint) - { - auto msg = NewI2NPTunnelMessage (endpoint); - msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; - return msg; - } + std::shared_ptr CreateEmptyTunnelDataMsg(bool endpoint) { + auto msg = NewI2NPTunnelMessage(endpoint); + msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; + return msg; + } - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) - { - auto msg = NewI2NPMessage (len); - uint8_t * payload = msg->GetPayload (); - htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); - htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); - msg->len += TUNNEL_GATEWAY_HEADER_SIZE; - if (msg->Concat (buf, len) < len) - LogPrint (eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen); - msg->FillI2NPMessageHeader (eI2NPTunnelGateway); - return msg; - } + std::shared_ptr CreateTunnelGatewayMsg(uint32_t tunnelID, const uint8_t *buf, size_t len) { + auto msg = NewI2NPMessage(len); + uint8_t *payload = msg->GetPayload(); + htobe32buf(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + htobe16buf(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + msg->len += TUNNEL_GATEWAY_HEADER_SIZE; + if (msg->Concat(buf, len) < len) + LogPrint(eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen); + msg->FillI2NPMessageHeader(eI2NPTunnelGateway); + return msg; + } - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg) - { - if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) - { - // message is capable to be used without copying - uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE; - htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); - int len = msg->GetLength (); - htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); - msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); - msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len; - msg->FillI2NPMessageHeader (eI2NPTunnelGateway); - return msg; - } - else - return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); - } + std::shared_ptr CreateTunnelGatewayMsg(uint32_t tunnelID, std::shared_ptr msg) { + if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) { + // message is capable to be used without copying + uint8_t *payload = msg->GetBuffer() - TUNNEL_GATEWAY_HEADER_SIZE; + htobe32buf(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + int len = msg->GetLength(); + htobe16buf(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); + msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + len; + msg->FillI2NPMessageHeader(eI2NPTunnelGateway); + return msg; + } else + return CreateTunnelGatewayMsg(tunnelID, msg->GetBuffer(), msg->GetLength()); + } - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, - const uint8_t * buf, size_t len, uint32_t replyMsgID) - { - auto msg = NewI2NPMessage (len); - size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; - msg->offset += gatewayMsgOffset; - msg->len += gatewayMsgOffset; - if (msg->Concat (buf, len) < len) - LogPrint (eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen); - msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message - len = msg->GetLength (); - msg->offset -= gatewayMsgOffset; - uint8_t * payload = msg->GetPayload (); - htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); - htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); - msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message - return msg; - } + std::shared_ptr CreateTunnelGatewayMsg(uint32_t tunnelID, I2NPMessageType msgType, + const uint8_t *buf, size_t len, uint32_t replyMsgID) { + auto msg = NewI2NPMessage(len); + size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; + msg->offset += gatewayMsgOffset; + msg->len += gatewayMsgOffset; + if (msg->Concat(buf, len) < len) + LogPrint(eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen); + msg->FillI2NPMessageHeader(msgType, replyMsgID); // create content message + len = msg->GetLength(); + msg->offset -= gatewayMsgOffset; + uint8_t *payload = msg->GetPayload(); + htobe32buf(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + htobe16buf(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + msg->FillI2NPMessageHeader(eI2NPTunnelGateway); // gateway message + return msg; + } - size_t GetI2NPMessageLength (const uint8_t * msg, size_t len) - { - if (len < I2NP_HEADER_SIZE_OFFSET + 2) - { - LogPrint (eLogError, "I2NP: Message length ", len, " is smaller than header"); - return len; - } - auto l = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE; - if (l > len) - { - LogPrint (eLogError, "I2NP: Message length ", l, " exceeds buffer length ", len); - l = len; - } - return l; - } + size_t GetI2NPMessageLength(const uint8_t *msg, size_t len) { + if (len < I2NP_HEADER_SIZE_OFFSET + 2) { + LogPrint(eLogError, "I2NP: Message length ", len, " is smaller than header"); + return len; + } + auto l = bufbe16toh(msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE; + if (l > len) { + LogPrint(eLogError, "I2NP: Message length ", l, " exceeds buffer length ", len); + l = len; + } + return l; + } - void HandleI2NPMessage (uint8_t * msg, size_t len) - { - if (len < I2NP_HEADER_SIZE) - { - LogPrint (eLogError, "I2NP: Message length ", len, " is smaller than header"); - return; - } - uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; - uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); - LogPrint (eLogDebug, "I2NP: Msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); - uint8_t * buf = msg + I2NP_HEADER_SIZE; - auto size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); - len -= I2NP_HEADER_SIZE; - if (size > len) - { - LogPrint (eLogError, "I2NP: Payload size ", size, " exceeds buffer length ", len); - size = len; - } - switch (typeID) - { - case eI2NPVariableTunnelBuild: - HandleVariableTunnelBuildMsg (msgID, buf, size); - break; - case eI2NPShortTunnelBuild: - HandleShortTunnelBuildMsg (msgID, buf, size); - break; - case eI2NPVariableTunnelBuildReply: - HandleTunnelBuildReplyMsg (msgID, buf, size, false); - break; - case eI2NPShortTunnelBuildReply: - HandleTunnelBuildReplyMsg (msgID, buf, size, true); - break; - case eI2NPTunnelBuild: - HandleTunnelBuildMsg (buf, size); - break; - case eI2NPTunnelBuildReply: - // TODO: - break; - default: - LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID); - } - } + void HandleI2NPMessage(uint8_t *msg, size_t len) { + if (len < I2NP_HEADER_SIZE) { + LogPrint(eLogError, "I2NP: Message length ", len, " is smaller than header"); + return; + } + uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; + uint32_t msgID = bufbe32toh(msg + I2NP_HEADER_MSGID_OFFSET); + LogPrint(eLogDebug, "I2NP: Msg received len=", len, ", type=", (int) typeID, ", msgID=", (unsigned int) msgID); + uint8_t *buf = msg + I2NP_HEADER_SIZE; + auto size = bufbe16toh(msg + I2NP_HEADER_SIZE_OFFSET); + len -= I2NP_HEADER_SIZE; + if (size > len) { + LogPrint(eLogError, "I2NP: Payload size ", size, " exceeds buffer length ", len); + size = len; + } + switch (typeID) { + case eI2NPVariableTunnelBuild: + HandleVariableTunnelBuildMsg(msgID, buf, size); + break; + case eI2NPShortTunnelBuild: + HandleShortTunnelBuildMsg(msgID, buf, size); + break; + case eI2NPVariableTunnelBuildReply: + HandleTunnelBuildReplyMsg(msgID, buf, size, false); + break; + case eI2NPShortTunnelBuildReply: + HandleTunnelBuildReplyMsg(msgID, buf, size, true); + break; + case eI2NPTunnelBuild: + HandleTunnelBuildMsg(buf, size); + break; + case eI2NPTunnelBuildReply: + // TODO: + break; + default: + LogPrint(eLogWarning, "I2NP: Unexpected message ", (int) typeID); + } + } - void HandleI2NPMessage (std::shared_ptr msg) - { - if (msg) - { - uint8_t typeID = msg->GetTypeID (); - LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID); - switch (typeID) - { - case eI2NPTunnelData: - i2p::tunnel::tunnels.PostTunnelData (msg); - break; - case eI2NPTunnelGateway: - i2p::tunnel::tunnels.PostTunnelData (msg); - break; - case eI2NPGarlic: - { - if (msg->from && msg->from->GetTunnelPool ()) - msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg); - else - i2p::context.ProcessGarlicMessage (msg); - break; - } - case eI2NPDatabaseStore: - case eI2NPDatabaseSearchReply: - case eI2NPDatabaseLookup: - // forward to netDb - i2p::data::netdb.PostI2NPMsg (msg); - break; - case eI2NPDeliveryStatus: - { - if (msg->from && msg->from->GetTunnelPool ()) - msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg); - else - i2p::context.ProcessDeliveryStatusMessage (msg); - break; - } - case eI2NPVariableTunnelBuild: - case eI2NPVariableTunnelBuildReply: - case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: - case eI2NPShortTunnelBuild: - case eI2NPShortTunnelBuildReply: - // forward to tunnel thread - i2p::tunnel::tunnels.PostTunnelData (msg); - break; - default: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); - } - } - } + void HandleI2NPMessage(std::shared_ptr msg) { + if (msg) { + uint8_t typeID = msg->GetTypeID(); + LogPrint(eLogDebug, "I2NP: Handling message with type ", (int) typeID); + switch (typeID) { + case eI2NPTunnelData: + i2p::tunnel::tunnels.PostTunnelData(msg); + break; + case eI2NPTunnelGateway: + i2p::tunnel::tunnels.PostTunnelData(msg); + break; + case eI2NPGarlic: { + if (msg->from && msg->from->GetTunnelPool()) + msg->from->GetTunnelPool()->ProcessGarlicMessage(msg); + else + i2p::context.ProcessGarlicMessage(msg); + break; + } + case eI2NPDatabaseStore: + case eI2NPDatabaseSearchReply: + case eI2NPDatabaseLookup: + // forward to netDb + i2p::data::netdb.PostI2NPMsg(msg); + break; + case eI2NPDeliveryStatus: { + if (msg->from && msg->from->GetTunnelPool()) + msg->from->GetTunnelPool()->ProcessDeliveryStatus(msg); + else + i2p::context.ProcessDeliveryStatusMessage(msg); + break; + } + case eI2NPVariableTunnelBuild: + case eI2NPVariableTunnelBuildReply: + case eI2NPTunnelBuild: + case eI2NPTunnelBuildReply: + case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuildReply: + // forward to tunnel thread + i2p::tunnel::tunnels.PostTunnelData(msg); + break; + default: + HandleI2NPMessage(msg->GetBuffer(), msg->GetLength()); + } + } + } - I2NPMessagesHandler::~I2NPMessagesHandler () - { - Flush (); - } + I2NPMessagesHandler::~I2NPMessagesHandler() { + Flush(); + } - void I2NPMessagesHandler::PutNextMessage (std::shared_ptr&& msg) - { - if (msg) - { - switch (msg->GetTypeID ()) - { - case eI2NPTunnelData: - m_TunnelMsgs.push_back (msg); - break; - case eI2NPTunnelGateway: - m_TunnelGatewayMsgs.push_back (msg); - break; - default: - HandleI2NPMessage (msg); - } - } - } + void I2NPMessagesHandler::PutNextMessage(std::shared_ptr &&msg) { + if (msg) { + switch (msg->GetTypeID()) { + case eI2NPTunnelData: + m_TunnelMsgs.push_back(msg); + break; + case eI2NPTunnelGateway: + m_TunnelGatewayMsgs.push_back(msg); + break; + default: + HandleI2NPMessage(msg); + } + } + } - void I2NPMessagesHandler::Flush () - { - if (!m_TunnelMsgs.empty ()) - { - i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs); - m_TunnelMsgs.clear (); - } - if (!m_TunnelGatewayMsgs.empty ()) - { - i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs); - m_TunnelGatewayMsgs.clear (); - } - } + void I2NPMessagesHandler::Flush() { + if (!m_TunnelMsgs.empty()) { + i2p::tunnel::tunnels.PostTunnelData(m_TunnelMsgs); + m_TunnelMsgs.clear(); + } + if (!m_TunnelGatewayMsgs.empty()) { + i2p::tunnel::tunnels.PostTunnelData(m_TunnelGatewayMsgs); + m_TunnelGatewayMsgs.clear(); + } + } } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index e60f6a9c..405388d1 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -19,299 +19,350 @@ #include "RouterInfo.h" #include "LeaseSet.h" -namespace i2p -{ - // I2NP header - 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_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4; - 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_SIZE = I2NP_HEADER_CHKS_OFFSET + 1; +namespace i2p { + // I2NP header + 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_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4; + 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_SIZE = I2NP_HEADER_CHKS_OFFSET + 1; - // I2NP short header - 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_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; + // I2NP short header + 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_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; - // I2NP NTCP2 header - const size_t I2NP_NTCP2_HEADER_SIZE = I2NP_HEADER_EXPIRATION_OFFSET + 4; + // I2NP NTCP2 header + const size_t I2NP_NTCP2_HEADER_SIZE = I2NP_HEADER_EXPIRATION_OFFSET + 4; - // Tunnel Gateway header - const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; - const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; - const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2; + // Tunnel Gateway header + const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; + const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; + const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2; - // DeliveryStatus - 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_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; + // DeliveryStatus + 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_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; - // DatabaseStore - 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_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1; - const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4; + // DatabaseStore + 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_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1; + const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4; - // TunnelBuild - const size_t TUNNEL_BUILD_RECORD_SIZE = 528; - const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218; + // TunnelBuild + const size_t TUNNEL_BUILD_RECORD_SIZE = 528; + const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218; - // BuildRequestRecordEncrypted - 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; + // BuildRequestRecordEncrypted + 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; - // ECIES BuildRequestRecordClearText - 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_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_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_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_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_EXPIRATION_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; - const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = 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 BuildRequestRecordClearText + 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_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_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_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_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_EXPIRATION_OFFSET = + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; + const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = + 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 - const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0; - const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; + // ECIES BuildResponseRecord + const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0; + const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; - // ShortRequestRecordClearText - 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_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_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_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_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_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; - const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154; + // ShortRequestRecordClearText + 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_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_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_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_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_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; + const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154; - // ShortResponseRecord - const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0; - const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; + // ShortResponseRecord + const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0; + const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; - enum I2NPMessageType - { - eI2NPDummyMsg = 0, - eI2NPDatabaseStore = 1, - eI2NPDatabaseLookup = 2, - eI2NPDatabaseSearchReply = 3, - eI2NPDeliveryStatus = 10, - eI2NPGarlic = 11, - eI2NPTunnelData = 18, - eI2NPTunnelGateway = 19, - eI2NPData = 20, - eI2NPTunnelBuild = 21, - eI2NPTunnelBuildReply = 22, - eI2NPVariableTunnelBuild = 23, - eI2NPVariableTunnelBuildReply = 24, - eI2NPShortTunnelBuild = 25, - eI2NPShortTunnelBuildReply = 26 - }; + enum I2NPMessageType { + eI2NPDummyMsg = 0, + eI2NPDatabaseStore = 1, + eI2NPDatabaseLookup = 2, + eI2NPDatabaseSearchReply = 3, + eI2NPDeliveryStatus = 10, + eI2NPGarlic = 11, + eI2NPTunnelData = 18, + eI2NPTunnelGateway = 19, + eI2NPData = 20, + eI2NPTunnelBuild = 21, + eI2NPTunnelBuildReply = 22, + eI2NPVariableTunnelBuild = 23, + eI2NPVariableTunnelBuildReply = 24, + eI2NPShortTunnelBuild = 25, + eI2NPShortTunnelBuildReply = 26 + }; - const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; - const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40; - const int NUM_TUNNEL_BUILD_RECORDS = 8; + const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; + const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40; + const int NUM_TUNNEL_BUILD_RECORDS = 8; - // DatabaseLookup flags - const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; - const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02; - const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10; - const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; - 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_ROUTERINFO_LOOKUP = 0x08; // 1000 - const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100 + // DatabaseLookup flags + const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; + const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02; + const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10; + const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; + 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_ROUTERINFO_LOOKUP = 0x08; // 1000 + const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100 -namespace tunnel -{ - class InboundTunnel; - class TunnelPool; -} + namespace tunnel { + class InboundTunnel; - const size_t I2NP_MAX_MESSAGE_SIZE = 62708; - 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 + class TunnelPool; + } - struct I2NPMessage - { - uint8_t * buf; - size_t len, offset, maxLen; - std::shared_ptr from; + const size_t I2NP_MAX_MESSAGE_SIZE = 62708; + 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 - I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), - offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header + struct I2NPMessage { + uint8_t *buf; + size_t len, offset, maxLen; + std::shared_ptr from; - // header accessors - uint8_t * GetHeader () { return GetBuffer (); }; - 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]; - } + I2NPMessage() : buf(nullptr), len(I2NP_HEADER_SIZE + 2), + offset(2), maxLen(0), from(nullptr) {}; // reserve 2 bytes for NTCP header - // payload - uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; }; - 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; }; + // header accessors + uint8_t *GetHeader() { return GetBuffer(); }; - void Align (size_t alignment) - { - if (len + alignment > maxLen) return; - size_t rem = ((size_t)GetBuffer ()) % alignment; - if (rem) - { - offset += (alignment - rem); - len += (alignment - rem); - } - } + const uint8_t *GetHeader() const { return GetBuffer(); }; - 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; - } + void SetTypeID(uint8_t typeID) { GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = typeID; }; - I2NPMessage& operator=(const I2NPMessage& other) - { - memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); - len = offset + other.GetLength (); - from = other.from; - return *this; - } + uint8_t GetTypeID() const { return GetHeader()[I2NP_HEADER_TYPEID_OFFSET]; }; - // 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 SetMsgID(uint32_t msgID) { htobe32buf(GetHeader() + I2NP_HEADER_MSGID_OFFSET, msgID); }; - 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 - } + uint32_t GetMsgID() const { return bufbe32toh(GetHeader() + I2NP_HEADER_MSGID_OFFSET); }; - void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true); - void RenewI2NPMessageHeader (); - bool IsExpired () const; - }; + void SetExpiration(uint64_t expiration) { + htobe64buf(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET, expiration); + }; - template - struct I2NPMessageBuffer: public I2NPMessage - { - I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; }; - uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding - }; + uint64_t GetExpiration() const { return bufbe64toh(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET); }; - std::shared_ptr NewI2NPMessage (); - std::shared_ptr NewI2NPShortMessage (); - std::shared_ptr NewI2NPTunnelMessage (bool endpoint); - std::shared_ptr NewI2NPMessage (size_t len); + void SetSize(uint16_t size) { htobe16buf(GetHeader() + I2NP_HEADER_SIZE_OFFSET, size); }; - std::shared_ptr CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); - std::shared_ptr CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from = nullptr); - std::shared_ptr CopyI2NPMessage (std::shared_ptr msg); + uint16_t GetSize() const { return bufbe16toh(GetHeader() + I2NP_HEADER_SIZE_OFFSET); }; - std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID); - std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, - uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); - std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, - const std::set& excludedFloodfills, - std::shared_ptr replyTunnel, - const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false); - std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector routers); + void UpdateSize() { SetSize(GetPayloadLength()); }; - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router = nullptr, uint32_t replyToken = 0, std::shared_ptr replyTunnel = nullptr); - std::shared_ptr CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr leaseSet); // for floodfill only - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken = 0, std::shared_ptr replyTunnel = nullptr); - bool IsRouterInfoMsg (std::shared_ptr msg); + void SetChks(uint8_t chks) { GetHeader()[I2NP_HEADER_CHKS_OFFSET] = chks; }; - std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf); - std::shared_ptr CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); - std::shared_ptr CreateEmptyTunnelDataMsg (bool endpoint); + void UpdateChks() { + uint8_t hash[32]; + SHA256(GetPayload(), GetPayloadLength(), hash); + GetHeader()[I2NP_HEADER_CHKS_OFFSET] = hash[0]; + } - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, - const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg); + // payload + uint8_t *GetPayload() { return GetBuffer() + I2NP_HEADER_SIZE; }; - size_t GetI2NPMessageLength (const uint8_t * msg, size_t len); - void HandleI2NPMessage (uint8_t * msg, size_t len); - void HandleI2NPMessage (std::shared_ptr msg); + const uint8_t *GetPayload() const { return GetBuffer() + I2NP_HEADER_SIZE; }; - class I2NPMessagesHandler - { - public: + uint8_t *GetBuffer() { return buf + offset; }; - ~I2NPMessagesHandler (); - void PutNextMessage (std::shared_ptr&& msg); - void Flush (); + const uint8_t *GetBuffer() const { return buf + offset; }; - private: + size_t GetLength() const { return len - offset; }; - std::vector > m_TunnelMsgs, m_TunnelGatewayMsgs; - }; + size_t GetPayloadLength() const { return GetLength() - I2NP_HEADER_SIZE; }; - const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500; - void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels); - uint16_t GetMaxNumTransitTunnels (); + void Align(size_t alignment) { + 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) { + // 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 + struct I2NPMessageBuffer : public I2NPMessage { + I2NPMessageBuffer() { + buf = m_Buffer; + maxLen = sz; + }; + uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding + }; + + std::shared_ptr NewI2NPMessage(); + + std::shared_ptr NewI2NPShortMessage(); + + std::shared_ptr NewI2NPTunnelMessage(bool endpoint); + + std::shared_ptr NewI2NPMessage(size_t len); + + std::shared_ptr + CreateI2NPMessage(I2NPMessageType msgType, const uint8_t *buf, size_t len, uint32_t replyMsgID = 0); + + std::shared_ptr + CreateI2NPMessage(const uint8_t *buf, size_t len, std::shared_ptr from = nullptr); + + std::shared_ptr CopyI2NPMessage(std::shared_ptr msg); + + std::shared_ptr CreateDeliveryStatusMsg(uint32_t msgID); + + std::shared_ptr CreateRouterInfoDatabaseLookupMsg(const uint8_t *key, const uint8_t *from, + uint32_t replyTunnelID, bool exploratory = false, + std::set *excludedPeers = nullptr); + + std::shared_ptr CreateLeaseSetDatabaseLookupMsg(const i2p::data::IdentHash &dest, + const std::set &excludedFloodfills, + std::shared_ptr replyTunnel, + const uint8_t *replyKey, const uint8_t *replyTag, + bool replyECIES = false); + + std::shared_ptr + CreateDatabaseSearchReply(const i2p::data::IdentHash &ident, std::vector routers); + + std::shared_ptr + CreateDatabaseStoreMsg(std::shared_ptr router = nullptr, uint32_t replyToken = 0, + std::shared_ptr replyTunnel = nullptr); + + std::shared_ptr CreateDatabaseStoreMsg(const i2p::data::IdentHash &storeHash, + std::shared_ptr leaseSet); // for floodfill only + std::shared_ptr + CreateDatabaseStoreMsg(std::shared_ptr leaseSet, uint32_t replyToken = 0, + std::shared_ptr replyTunnel = nullptr); + + bool IsRouterInfoMsg(std::shared_ptr msg); + + std::shared_ptr CreateTunnelDataMsg(const uint8_t *buf); + + std::shared_ptr CreateTunnelDataMsg(uint32_t tunnelID, const uint8_t *payload); + + std::shared_ptr CreateEmptyTunnelDataMsg(bool endpoint); + + std::shared_ptr CreateTunnelGatewayMsg(uint32_t tunnelID, const uint8_t *buf, size_t len); + + std::shared_ptr CreateTunnelGatewayMsg(uint32_t tunnelID, I2NPMessageType msgType, + const uint8_t *buf, size_t len, uint32_t replyMsgID = 0); + + std::shared_ptr CreateTunnelGatewayMsg(uint32_t tunnelID, std::shared_ptr msg); + + size_t GetI2NPMessageLength(const uint8_t *msg, size_t len); + + void HandleI2NPMessage(uint8_t *msg, size_t len); + + void HandleI2NPMessage(std::shared_ptr msg); + + class I2NPMessagesHandler { + public: + + ~I2NPMessagesHandler(); + + void PutNextMessage(std::shared_ptr &&msg); + + void Flush(); + + private: + + std::vector > m_TunnelMsgs, m_TunnelGatewayMsgs; + }; + + const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500; + + void SetMaxNumTransitTunnels(uint16_t maxNumTransitTunnels); + + uint16_t GetMaxNumTransitTunnels(); } #endif diff --git a/libi2pd/I2PEndian.cpp b/libi2pd/I2PEndian.cpp index 5496f144..db0f8bda 100644 --- a/libi2pd/I2PEndian.cpp +++ b/libi2pd/I2PEndian.cpp @@ -14,39 +14,35 @@ #include "LittleBigEndian.h" #ifdef NEEDS_LOCAL_ENDIAN -uint16_t htobe16(uint16_t int16) -{ - BigEndian u16(int16); - return u16.raw_value; + +uint16_t htobe16(uint16_t int16) { + BigEndian u16(int16); + return u16.raw_value; } -uint32_t htobe32(uint32_t int32) -{ - BigEndian u32(int32); - return u32.raw_value; +uint32_t htobe32(uint32_t int32) { + BigEndian u32(int32); + return u32.raw_value; } -uint64_t htobe64(uint64_t int64) -{ - BigEndian u64(int64); - return u64.raw_value; +uint64_t htobe64(uint64_t int64) { + BigEndian u64(int64); + return u64.raw_value; } -uint16_t be16toh(uint16_t big16) -{ - LittleEndian u16(big16); - return u16.raw_value; +uint16_t be16toh(uint16_t big16) { + LittleEndian u16(big16); + return u16.raw_value; } -uint32_t be32toh(uint32_t big32) -{ - LittleEndian u32(big32); - return u32.raw_value; +uint32_t be32toh(uint32_t big32) { + LittleEndian u32(big32); + return u32.raw_value; } -uint64_t be64toh(uint64_t big64) -{ - LittleEndian u64(big64); - return u64.raw_value; +uint64_t be64toh(uint64_t big64) { + LittleEndian u64(big64); + return u64.raw_value; } + #endif diff --git a/libi2pd/I2PEndian.h b/libi2pd/I2PEndian.h index d97bd055..a7a53ee2 100644 --- a/libi2pd/I2PEndian.h +++ b/libi2pd/I2PEndian.h @@ -8,6 +8,7 @@ #ifndef I2PENDIAN_H__ #define I2PENDIAN_H__ + #include #include @@ -53,13 +54,19 @@ #else #define NEEDS_LOCAL_ENDIAN + #include + uint16_t htobe16(uint16_t int16); + uint32_t htobe32(uint32_t int32); + uint64_t htobe64(uint64_t int64); uint16_t be16toh(uint16_t big16); + uint32_t be32toh(uint32_t big32); + uint64_t be64toh(uint64_t big64); // assume LittleEndine @@ -72,100 +79,82 @@ uint64_t be64toh(uint64_t big64); #endif -inline uint16_t buf16toh(const void *buf) -{ - uint16_t b16; - memcpy(&b16, buf, sizeof(uint16_t)); - return b16; +inline uint16_t buf16toh(const void *buf) { + uint16_t b16; + memcpy(&b16, buf, sizeof(uint16_t)); + return b16; } -inline uint32_t buf32toh(const void *buf) -{ - uint32_t b32; - memcpy(&b32, buf, sizeof(uint32_t)); - return b32; +inline uint32_t buf32toh(const void *buf) { + uint32_t b32; + memcpy(&b32, buf, sizeof(uint32_t)); + return b32; } -inline uint64_t buf64toh(const void *buf) -{ - uint64_t b64; - memcpy(&b64, buf, sizeof(uint64_t)); - return b64; +inline uint64_t buf64toh(const void *buf) { + uint64_t b64; + memcpy(&b64, buf, sizeof(uint64_t)); + return b64; } -inline uint16_t bufbe16toh(const void *buf) -{ - return be16toh(buf16toh(buf)); +inline uint16_t bufbe16toh(const void *buf) { + return be16toh(buf16toh(buf)); } -inline uint32_t bufbe32toh(const void *buf) -{ - return be32toh(buf32toh(buf)); +inline uint32_t bufbe32toh(const void *buf) { + return be32toh(buf32toh(buf)); } -inline uint64_t bufbe64toh(const void *buf) -{ - return be64toh(buf64toh(buf)); +inline uint64_t bufbe64toh(const void *buf) { + return be64toh(buf64toh(buf)); } -inline void htobuf16(void *buf, uint16_t b16) -{ - memcpy(buf, &b16, sizeof(uint16_t)); +inline void htobuf16(void *buf, uint16_t b16) { + memcpy(buf, &b16, sizeof(uint16_t)); } -inline void htobuf32(void *buf, uint32_t b32) -{ - memcpy(buf, &b32, sizeof(uint32_t)); +inline void htobuf32(void *buf, uint32_t b32) { + memcpy(buf, &b32, sizeof(uint32_t)); } -inline void htobuf64(void *buf, uint64_t b64) -{ - memcpy(buf, &b64, sizeof(uint64_t)); +inline void htobuf64(void *buf, uint64_t b64) { + memcpy(buf, &b64, sizeof(uint64_t)); } -inline void htobe16buf(void *buf, uint16_t big16) -{ - htobuf16(buf, htobe16(big16)); +inline void htobe16buf(void *buf, uint16_t big16) { + htobuf16(buf, htobe16(big16)); } -inline void htobe32buf(void *buf, uint32_t big32) -{ - htobuf32(buf, htobe32(big32)); +inline void htobe32buf(void *buf, uint32_t big32) { + htobuf32(buf, htobe32(big32)); } -inline void htobe64buf(void *buf, uint64_t big64) -{ - htobuf64(buf, htobe64(big64)); +inline void htobe64buf(void *buf, uint64_t big64) { + htobuf64(buf, htobe64(big64)); } -inline void htole16buf(void *buf, uint16_t big16) -{ - htobuf16(buf, htole16(big16)); +inline void htole16buf(void *buf, uint16_t big16) { + htobuf16(buf, htole16(big16)); } -inline void htole32buf(void *buf, uint32_t big32) -{ - htobuf32(buf, htole32(big32)); +inline void htole32buf(void *buf, uint32_t big32) { + htobuf32(buf, htole32(big32)); } -inline void htole64buf(void *buf, uint64_t big64) -{ - htobuf64(buf, htole64(big64)); +inline void htole64buf(void *buf, uint64_t big64) { + htobuf64(buf, htole64(big64)); } -inline uint16_t bufle16toh(const void *buf) -{ - return le16toh(buf16toh(buf)); +inline uint16_t bufle16toh(const void *buf) { + return le16toh(buf16toh(buf)); } -inline uint32_t bufle32toh(const void *buf) -{ - return le32toh(buf32toh(buf)); +inline uint32_t bufle32toh(const void *buf) { + return le32toh(buf32toh(buf)); } -inline uint64_t bufle64toh(const void *buf) -{ - return le64toh(buf64toh(buf)); +inline uint64_t bufle64toh(const void *buf) { + return le64toh(buf64toh(buf)); } #endif // I2PENDIAN_H__ diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index cff0c37d..bda6695c 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -12,839 +12,766 @@ #include "Timestamp.h" #include "Identity.h" -namespace i2p -{ -namespace data -{ - Identity& Identity::operator=(const Keys& keys) - { - // copy public and signing keys together - memcpy (publicKey, keys.publicKey, sizeof (publicKey)); - memcpy (signingKey, keys.signingKey, sizeof (signingKey)); - memset (certificate, 0, sizeof (certificate)); - return *this; - } +namespace i2p { + namespace data { + Identity &Identity::operator=(const Keys &keys) { + // copy public and signing keys together + memcpy(publicKey, keys.publicKey, sizeof(publicKey)); + memcpy(signingKey, keys.signingKey, sizeof(signingKey)); + memset(certificate, 0, sizeof(certificate)); + return *this; + } - size_t Identity::FromBuffer (const uint8_t * buf, size_t len) - { - if ( len < DEFAULT_IDENTITY_SIZE ) { - // buffer too small, don't overflow - return 0; - } - memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE); - return DEFAULT_IDENTITY_SIZE; - } + size_t Identity::FromBuffer(const uint8_t *buf, size_t len) { + if (len < DEFAULT_IDENTITY_SIZE) { + // buffer too small, don't overflow + return 0; + } + memcpy(publicKey, buf, DEFAULT_IDENTITY_SIZE); + return DEFAULT_IDENTITY_SIZE; + } - IdentHash Identity::Hash () const - { - IdentHash hash; - SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash); - return hash; - } + IdentHash Identity::Hash() const { + IdentHash hash; + SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash); + return hash; + } - IdentityEx::IdentityEx (): - m_ExtendedLen (0) - { - } + IdentityEx::IdentityEx() : + m_ExtendedLen(0) { + } - IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType) - { - if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - { - memcpy (m_StandardIdentity.publicKey, publicKey, 32); - RAND_bytes (m_StandardIdentity.publicKey + 32, 224); - } - else - memcpy (m_StandardIdentity.publicKey, publicKey, 256); - if (type != SIGNING_KEY_TYPE_DSA_SHA1) - { - size_t excessLen = 0; - uint8_t * excessBuf = nullptr; - switch (type) - { - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - { - size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 - RAND_bytes (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); - break; - } - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - { - size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 - RAND_bytes (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH); - break; - } - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - { - memcpy (m_StandardIdentity.signingKey, signingKey, 128); - excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128 - excessBuf = new uint8_t[excessLen]; - memcpy (excessBuf, signingKey + 128, excessLen); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - LogPrint (eLogError, "Identity: RSA signing key type ", (int)type, " is not supported"); - break; - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - { - size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 - RAND_bytes (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); - break; - } - case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: - { - // 256 - size_t padding = 128 - i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH; // 64 = 128 - 64 - RAND_bytes (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH); - break; - } - case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: - { - // 512 - // no padding, key length is 128 - memcpy (m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH); - break; - } - default: - LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported"); - } - m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length - // fill certificate - m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY; - htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen); - // fill extended buffer - htobe16buf (m_ExtendedBuffer, type); - htobe16buf (m_ExtendedBuffer + 2, cryptoType); - if (excessLen && excessBuf) - { - if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4) - { - LogPrint (eLogError, "Identity: Unexpected excessive signing key len ", excessLen); - excessLen = MAX_EXTENDED_BUFFER_SIZE - 4; - } - memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); - delete[] excessBuf; - } - // calculate ident hash - RecalculateIdentHash(); - } - else // DSA-SHA1 - { - memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey)); - memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); - m_IdentHash = m_StandardIdentity.Hash (); - m_ExtendedLen = 0; - } - CreateVerifier (); - } + IdentityEx::IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey, SigningKeyType type, + CryptoKeyType cryptoType) { + if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) { + memcpy(m_StandardIdentity.publicKey, publicKey, 32); + RAND_bytes(m_StandardIdentity.publicKey + 32, 224); + } else + memcpy(m_StandardIdentity.publicKey, publicKey, 256); + if (type != SIGNING_KEY_TYPE_DSA_SHA1) { + size_t excessLen = 0; + uint8_t *excessBuf = nullptr; + switch (type) { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: { + size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 + RAND_bytes(m_StandardIdentity.signingKey, padding); + memcpy(m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: { + size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 + RAND_bytes(m_StandardIdentity.signingKey, padding); + memcpy(m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: { + memcpy(m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy(excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + LogPrint(eLogError, "Identity: RSA signing key type ", (int) type, " is not supported"); + break; + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: { + size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 + RAND_bytes(m_StandardIdentity.signingKey, padding); + memcpy(m_StandardIdentity.signingKey + padding, signingKey, + i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: { + // 256 + size_t padding = 128 - i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH; // 64 = 128 - 64 + RAND_bytes(m_StandardIdentity.signingKey, padding); + memcpy(m_StandardIdentity.signingKey + padding, signingKey, + i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: { + // 512 + // no padding, key length is 128 + memcpy(m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH); + break; + } + default: + LogPrint(eLogError, "Identity: Signing key type ", (int) type, " is not supported"); + } + m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length + // fill certificate + m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY; + htobe16buf(m_StandardIdentity.certificate + 1, m_ExtendedLen); + // fill extended buffer + htobe16buf(m_ExtendedBuffer, type); + htobe16buf(m_ExtendedBuffer + 2, cryptoType); + if (excessLen && excessBuf) { + if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4) { + LogPrint(eLogError, "Identity: Unexpected excessive signing key len ", excessLen); + excessLen = MAX_EXTENDED_BUFFER_SIZE - 4; + } + memcpy(m_ExtendedBuffer + 4, excessBuf, excessLen); + delete[] excessBuf; + } + // calculate ident hash + RecalculateIdentHash(); + } else // DSA-SHA1 + { + memcpy(m_StandardIdentity.signingKey, signingKey, sizeof(m_StandardIdentity.signingKey)); + memset(m_StandardIdentity.certificate, 0, sizeof(m_StandardIdentity.certificate)); + m_IdentHash = m_StandardIdentity.Hash(); + m_ExtendedLen = 0; + } + CreateVerifier(); + } - void IdentityEx::RecalculateIdentHash(uint8_t * buf) - { - bool dofree = buf == nullptr; - size_t sz = GetFullLen(); - if(!buf) - buf = new uint8_t[sz]; - ToBuffer (buf, sz); - SHA256(buf, sz, m_IdentHash); - if(dofree) - delete[] buf; - } + void IdentityEx::RecalculateIdentHash(uint8_t *buf) { + bool dofree = buf == nullptr; + size_t sz = GetFullLen(); + if (!buf) + buf = new uint8_t[sz]; + ToBuffer(buf, sz); + SHA256(buf, sz, m_IdentHash); + if (dofree) + delete[] buf; + } - IdentityEx::IdentityEx (const uint8_t * buf, size_t len): - m_ExtendedLen (0) - { - FromBuffer (buf, len); - } + IdentityEx::IdentityEx(const uint8_t *buf, size_t len) : + m_ExtendedLen(0) { + FromBuffer(buf, len); + } - IdentityEx::IdentityEx (const IdentityEx& other): - m_ExtendedLen (0) - { - *this = other; - } + IdentityEx::IdentityEx(const IdentityEx &other) : + m_ExtendedLen(0) { + *this = other; + } - IdentityEx::IdentityEx (const Identity& standard): - m_ExtendedLen (0) - { - *this = standard; - } + IdentityEx::IdentityEx(const Identity &standard) : + m_ExtendedLen(0) { + *this = standard; + } - IdentityEx::~IdentityEx () - { - delete m_Verifier; - } + IdentityEx::~IdentityEx() { + delete m_Verifier; + } - IdentityEx& IdentityEx::operator=(const IdentityEx& other) - { - memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); - m_IdentHash = other.m_IdentHash; + IdentityEx &IdentityEx::operator=(const IdentityEx &other) { + memcpy(&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); + m_IdentHash = other.m_IdentHash; - m_ExtendedLen = other.m_ExtendedLen; - if (m_ExtendedLen > 0) - { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; - memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); - } + m_ExtendedLen = other.m_ExtendedLen; + if (m_ExtendedLen > 0) { + if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; + memcpy(m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); + } - delete m_Verifier; - m_Verifier = nullptr; + delete m_Verifier; + m_Verifier = nullptr; - return *this; - } + return *this; + } - IdentityEx& IdentityEx::operator=(const Identity& standard) - { - m_StandardIdentity = standard; - m_IdentHash = m_StandardIdentity.Hash (); + IdentityEx &IdentityEx::operator=(const Identity &standard) { + m_StandardIdentity = standard; + m_IdentHash = m_StandardIdentity.Hash(); - m_ExtendedLen = 0; + m_ExtendedLen = 0; - delete m_Verifier; - m_Verifier = nullptr; + delete m_Verifier; + m_Verifier = nullptr; - return *this; - } + return *this; + } - size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len) - { - if (len < DEFAULT_IDENTITY_SIZE) - { - LogPrint (eLogError, "Identity: Buffer length ", len, " is too small"); - return 0; - } - memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); + size_t IdentityEx::FromBuffer(const uint8_t *buf, size_t len) { + if (len < DEFAULT_IDENTITY_SIZE) { + LogPrint(eLogError, "Identity: Buffer length ", len, " is too small"); + return 0; + } + memcpy(&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); - m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1); - if (m_ExtendedLen) - { - if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) - { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; - memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); - } - else - { - LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE); - m_ExtendedLen = 0; - return 0; - } - } - else - m_ExtendedLen = 0; - SHA256(buf, GetFullLen (), m_IdentHash); + m_ExtendedLen = bufbe16toh(m_StandardIdentity.certificate + 1); + if (m_ExtendedLen) { + if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) { + if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; + memcpy(m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); + } else { + LogPrint(eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", + len - DEFAULT_IDENTITY_SIZE); + m_ExtendedLen = 0; + return 0; + } + } else + m_ExtendedLen = 0; + SHA256(buf, GetFullLen(), m_IdentHash); - delete m_Verifier; - m_Verifier = nullptr; + delete m_Verifier; + m_Verifier = nullptr; - return GetFullLen (); - } + return GetFullLen(); + } - size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const - { - const size_t fullLen = GetFullLen(); - if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else - memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); - if (m_ExtendedLen > 0) - memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); - return fullLen; - } + size_t IdentityEx::ToBuffer(uint8_t *buf, size_t len) const { + const size_t fullLen = GetFullLen(); + if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else + memcpy(buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); + if (m_ExtendedLen > 0) + memcpy(buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); + return fullLen; + } - size_t IdentityEx::FromBase64(const std::string& s) - { - const size_t slen = s.length(); - std::vector buf(slen); // binary data can't exceed base64 - const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen); - return FromBuffer (buf.data(), len); - } + size_t IdentityEx::FromBase64(const std::string &s) { + const size_t slen = s.length(); + std::vector buf(slen); // binary data can't exceed base64 + const size_t len = Base64ToByteStream(s.c_str(), slen, buf.data(), slen); + return FromBuffer(buf.data(), len); + } - std::string IdentityEx::ToBase64 () const - { - const size_t bufLen = GetFullLen(); - const size_t strLen = Base64EncodingBufferSize(bufLen); - std::vector buf(bufLen); - std::vector str(strLen); - size_t l = ToBuffer (buf.data(), bufLen); - size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen); - return std::string (str.data(), l1); - } + std::string IdentityEx::ToBase64() const { + const size_t bufLen = GetFullLen(); + const size_t strLen = Base64EncodingBufferSize(bufLen); + std::vector buf(bufLen); + std::vector str(strLen); + size_t l = ToBuffer(buf.data(), bufLen); + size_t l1 = i2p::data::ByteStreamToBase64(buf.data(), l, str.data(), strLen); + return std::string(str.data(), l1); + } - size_t IdentityEx::GetSigningPublicKeyLen () const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->GetPublicKeyLen (); - return 128; - } + size_t IdentityEx::GetSigningPublicKeyLen() const { + if (!m_Verifier) CreateVerifier(); + if (m_Verifier) + return m_Verifier->GetPublicKeyLen(); + return 128; + } - const uint8_t * IdentityEx::GetSigningPublicKeyBuffer () const - { - auto keyLen = GetSigningPublicKeyLen (); - if (keyLen > 128) return nullptr; // P521 - return m_StandardIdentity.signingKey + 128 - keyLen; - } + const uint8_t *IdentityEx::GetSigningPublicKeyBuffer() const { + auto keyLen = GetSigningPublicKeyLen(); + if (keyLen > 128) return nullptr; // P521 + return m_StandardIdentity.signingKey + 128 - keyLen; + } - size_t IdentityEx::GetSigningPrivateKeyLen () const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->GetPrivateKeyLen (); - return GetSignatureLen ()/2; - } + size_t IdentityEx::GetSigningPrivateKeyLen() const { + if (!m_Verifier) CreateVerifier(); + if (m_Verifier) + return m_Verifier->GetPrivateKeyLen(); + return GetSignatureLen() / 2; + } - size_t IdentityEx::GetSignatureLen () const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->GetSignatureLen (); - return i2p::crypto::DSA_SIGNATURE_LENGTH; - } - bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->Verify (buf, len, signature); - return false; - } + size_t IdentityEx::GetSignatureLen() const { + if (!m_Verifier) CreateVerifier(); + if (m_Verifier) + return m_Verifier->GetSignatureLen(); + return i2p::crypto::DSA_SIGNATURE_LENGTH; + } - SigningKeyType IdentityEx::GetSigningKeyType () const - { - if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 2) - return bufbe16toh (m_ExtendedBuffer); // signing key - return SIGNING_KEY_TYPE_DSA_SHA1; - } + bool IdentityEx::Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const { + if (!m_Verifier) CreateVerifier(); + if (m_Verifier) + return m_Verifier->Verify(buf, len, signature); + return false; + } - bool IdentityEx::IsRSA () const - { - auto sigType = GetSigningKeyType (); - return sigType <= SIGNING_KEY_TYPE_RSA_SHA512_4096 && sigType >= SIGNING_KEY_TYPE_RSA_SHA256_2048; - } + SigningKeyType IdentityEx::GetSigningKeyType() const { + if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 2) + return bufbe16toh(m_ExtendedBuffer); // signing key + return SIGNING_KEY_TYPE_DSA_SHA1; + } - CryptoKeyType IdentityEx::GetCryptoKeyType () const - { - if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 4) - return bufbe16toh (m_ExtendedBuffer + 2); // crypto key - return CRYPTO_KEY_TYPE_ELGAMAL; - } + bool IdentityEx::IsRSA() const { + auto sigType = GetSigningKeyType(); + return sigType <= SIGNING_KEY_TYPE_RSA_SHA512_4096 && sigType >= SIGNING_KEY_TYPE_RSA_SHA256_2048; + } - i2p::crypto::Verifier * IdentityEx::CreateVerifier (SigningKeyType keyType) - { - switch (keyType) - { - case SIGNING_KEY_TYPE_DSA_SHA1: - return new i2p::crypto::DSAVerifier (); - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - return new i2p::crypto::ECDSAP256Verifier (); - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - return new i2p::crypto::ECDSAP384Verifier (); - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - return new i2p::crypto::ECDSAP521Verifier (); - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - return new i2p::crypto::EDDSA25519Verifier (); - case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: - return new i2p::crypto::GOSTR3410_256_Verifier (i2p::crypto::eGOSTR3410CryptoProA); - case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: - return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512); - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - return new i2p::crypto::RedDSA25519Verifier (); - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - LogPrint (eLogError, "Identity: RSA signing key type ", (int)keyType, " is not supported"); - break; - default: - LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported"); - } - return nullptr; - } + CryptoKeyType IdentityEx::GetCryptoKeyType() const { + if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 4) + return bufbe16toh(m_ExtendedBuffer + 2); // crypto key + return CRYPTO_KEY_TYPE_ELGAMAL; + } - void IdentityEx::CreateVerifier () const - { - if (m_Verifier) return; // don't create again - auto verifier = CreateVerifier (GetSigningKeyType ()); - if (verifier) - { - auto keyLen = verifier->GetPublicKeyLen (); - if (keyLen <= 128) - verifier->SetPublicKey (m_StandardIdentity.signingKey + 128 - keyLen); - else - { - // for P521 - uint8_t * signingKey = new uint8_t[keyLen]; - memcpy (signingKey, m_StandardIdentity.signingKey, 128); - size_t excessLen = keyLen - 128; - memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types - verifier->SetPublicKey (signingKey); - delete[] signingKey; - } - } - UpdateVerifier (verifier); - } + i2p::crypto::Verifier *IdentityEx::CreateVerifier(SigningKeyType keyType) { + switch (keyType) { + case SIGNING_KEY_TYPE_DSA_SHA1: + return new i2p::crypto::DSAVerifier(); + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + return new i2p::crypto::ECDSAP256Verifier(); + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + return new i2p::crypto::ECDSAP384Verifier(); + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + return new i2p::crypto::ECDSAP521Verifier(); + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + return new i2p::crypto::EDDSA25519Verifier(); + case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: + return new i2p::crypto::GOSTR3410_256_Verifier(i2p::crypto::eGOSTR3410CryptoProA); + case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: + return new i2p::crypto::GOSTR3410_512_Verifier(i2p::crypto::eGOSTR3410TC26A512); + case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: + return new i2p::crypto::RedDSA25519Verifier(); + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + LogPrint(eLogError, "Identity: RSA signing key type ", (int) keyType, " is not supported"); + break; + default: + LogPrint(eLogError, "Identity: Signing key type ", (int) keyType, " is not supported"); + } + return nullptr; + } - void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const - { - bool del = false; - { - std::lock_guard l(m_VerifierMutex); - if (!m_Verifier) - m_Verifier = verifier; - else - del = true; - } - if (del) - delete verifier; - } + void IdentityEx::CreateVerifier() const { + if (m_Verifier) return; // don't create again + auto verifier = CreateVerifier(GetSigningKeyType()); + if (verifier) { + auto keyLen = verifier->GetPublicKeyLen(); + if (keyLen <= 128) + verifier->SetPublicKey(m_StandardIdentity.signingKey + 128 - keyLen); + else { + // for P521 + uint8_t *signingKey = new uint8_t[keyLen]; + memcpy(signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = keyLen - 128; + memcpy(signingKey + 128, m_ExtendedBuffer + 4, + excessLen); // right after signing and crypto key types + verifier->SetPublicKey(signingKey); + delete[] signingKey; + } + } + UpdateVerifier(verifier); + } - void IdentityEx::DropVerifier () const - { - i2p::crypto::Verifier * verifier; - { - std::lock_guard l(m_VerifierMutex); - verifier = m_Verifier; - m_Verifier = nullptr; - } - delete verifier; - } + void IdentityEx::UpdateVerifier(i2p::crypto::Verifier *verifier) const { + bool del = false; + { + std::lock_guard l(m_VerifierMutex); + if (!m_Verifier) + m_Verifier = verifier; + else + del = true; + } + if (del) + delete verifier; + } - std::shared_ptr IdentityEx::CreateEncryptor (CryptoKeyType keyType, const uint8_t * key) - { - switch (keyType) - { - case CRYPTO_KEY_TYPE_ELGAMAL: - return std::make_shared(key); - break; - case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: - return std::make_shared(key); - break; - case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: - case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: - return std::make_shared(key); - break; - case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: - return std::make_shared(key); - break; - default: - LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType); - }; - return nullptr; - } + void IdentityEx::DropVerifier() const { + i2p::crypto::Verifier *verifier; + { + std::lock_guard l(m_VerifierMutex); + verifier = m_Verifier; + m_Verifier = nullptr; + } + delete verifier; + } - std::shared_ptr IdentityEx::CreateEncryptor (const uint8_t * key) const - { - if (!key) key = GetEncryptionPublicKey (); // use publicKey - return CreateEncryptor (GetCryptoKeyType (), key); - } + std::shared_ptr + IdentityEx::CreateEncryptor(CryptoKeyType keyType, const uint8_t *key) { + switch (keyType) { + case CRYPTO_KEY_TYPE_ELGAMAL: + return std::make_shared(key); + break; + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: + return std::make_shared(key); + break; + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: + return std::make_shared(key); + break; + case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: + return std::make_shared(key); + break; + default: + LogPrint(eLogError, "Identity: Unknown crypto key type ", (int) keyType); + }; + return nullptr; + } - PrivateKeys& PrivateKeys::operator=(const Keys& keys) - { - m_Public = std::make_shared(Identity (keys)); - memcpy (m_PrivateKey, keys.privateKey, 256); // 256 - memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ()); - m_OfflineSignature.resize (0); - m_TransientSignatureLen = 0; - m_TransientSigningPrivateKeyLen = 0; - m_Signer = nullptr; - CreateSigner (); - return *this; - } + std::shared_ptr IdentityEx::CreateEncryptor(const uint8_t *key) const { + if (!key) key = GetEncryptionPublicKey(); // use publicKey + return CreateEncryptor(GetCryptoKeyType(), key); + } - PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other) - { - m_Public = std::make_shared(*other.m_Public); - memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256 - m_OfflineSignature = other.m_OfflineSignature; - m_TransientSignatureLen = other.m_TransientSignatureLen; - m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen; - memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen : m_Public->GetSigningPrivateKeyLen ()); - m_Signer = nullptr; - CreateSigner (); - return *this; - } + PrivateKeys &PrivateKeys::operator=(const Keys &keys) { + m_Public = std::make_shared(Identity(keys)); + memcpy(m_PrivateKey, keys.privateKey, 256); // 256 + memcpy(m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen()); + m_OfflineSignature.resize(0); + m_TransientSignatureLen = 0; + m_TransientSigningPrivateKeyLen = 0; + m_Signer = nullptr; + CreateSigner(); + return *this; + } - size_t PrivateKeys::GetFullLen () const - { - size_t ret = m_Public->GetFullLen () + GetPrivateKeyLen () + m_Public->GetSigningPrivateKeyLen (); - if (IsOfflineSignature ()) - ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen; - return ret; - } + PrivateKeys &PrivateKeys::operator=(const PrivateKeys &other) { + m_Public = std::make_shared(*other.m_Public); + memcpy(m_PrivateKey, other.m_PrivateKey, 256); // 256 + m_OfflineSignature = other.m_OfflineSignature; + m_TransientSignatureLen = other.m_TransientSignatureLen; + m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen; + memcpy(m_SigningPrivateKey, other.m_SigningPrivateKey, + m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen + : m_Public->GetSigningPrivateKeyLen()); + m_Signer = nullptr; + CreateSigner(); + return *this; + } - size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len) - { - m_Public = std::make_shared(); - size_t ret = m_Public->FromBuffer (buf, len); - auto cryptoKeyLen = GetPrivateKeyLen (); - if (!ret || ret + cryptoKeyLen > len) return 0; // overflow - memcpy (m_PrivateKey, buf + ret, cryptoKeyLen); - ret += cryptoKeyLen; - size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); - if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow - memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); - ret += signingPrivateKeySize; - m_Signer = nullptr; - // check if signing private key is all zeros - bool allzeros = true; - for (size_t i = 0; i < signingPrivateKeySize; i++) - if (m_SigningPrivateKey[i]) - { - allzeros = false; - break; - } - if (allzeros) - { - // offline information - const uint8_t * offlineInfo = buf + ret; - ret += 4; // expires timestamp - SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type - std::unique_ptr transientVerifier (IdentityEx::CreateVerifier (keyType)); - if (!transientVerifier) return 0; - auto keyLen = transientVerifier->GetPublicKeyLen (); - if (keyLen + ret > len) return 0; - transientVerifier->SetPublicKey (buf + ret); ret += keyLen; - if (m_Public->GetSignatureLen () + ret > len) return 0; - if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret)) - { - LogPrint (eLogError, "Identity: Offline signature verification failed"); - return 0; - } - ret += m_Public->GetSignatureLen (); - m_TransientSignatureLen = transientVerifier->GetSignatureLen (); - // copy offline signature - size_t offlineInfoLen = buf + ret - offlineInfo; - m_OfflineSignature.resize (offlineInfoLen); - memcpy (m_OfflineSignature.data (), offlineInfo, offlineInfoLen); - // override signing private key - m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen (); - if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0; - memcpy (m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen); - ret += m_TransientSigningPrivateKeyLen; - CreateSigner (keyType); - } - else - CreateSigner (m_Public->GetSigningKeyType ()); - return ret; - } + size_t PrivateKeys::GetFullLen() const { + size_t ret = m_Public->GetFullLen() + GetPrivateKeyLen() + m_Public->GetSigningPrivateKeyLen(); + if (IsOfflineSignature()) + ret += m_OfflineSignature.size() + m_TransientSigningPrivateKeyLen; + return ret; + } - size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const - { - size_t ret = m_Public->ToBuffer (buf, len); - auto cryptoKeyLen = GetPrivateKeyLen (); - memcpy (buf + ret, m_PrivateKey, cryptoKeyLen); - ret += cryptoKeyLen; - size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); - if(ret + signingPrivateKeySize > len) return 0; // overflow - if (IsOfflineSignature ()) - memset (buf + ret, 0, signingPrivateKeySize); - else - memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); - ret += signingPrivateKeySize; - if (IsOfflineSignature ()) - { - // offline signature - auto offlineSignatureLen = m_OfflineSignature.size (); - if (ret + offlineSignatureLen > len) return 0; - memcpy (buf + ret, m_OfflineSignature.data (), offlineSignatureLen); - ret += offlineSignatureLen; - // transient private key - if (ret + m_TransientSigningPrivateKeyLen > len) return 0; - memcpy (buf + ret, m_SigningPrivateKey, m_TransientSigningPrivateKeyLen); - ret += m_TransientSigningPrivateKeyLen; - } - return ret; - } + size_t PrivateKeys::FromBuffer(const uint8_t *buf, size_t len) { + m_Public = std::make_shared(); + size_t ret = m_Public->FromBuffer(buf, len); + auto cryptoKeyLen = GetPrivateKeyLen(); + if (!ret || ret + cryptoKeyLen > len) return 0; // overflow + memcpy(m_PrivateKey, buf + ret, cryptoKeyLen); + ret += cryptoKeyLen; + size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen(); + if (signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow + memcpy(m_SigningPrivateKey, buf + ret, signingPrivateKeySize); + ret += signingPrivateKeySize; + m_Signer = nullptr; + // check if signing private key is all zeros + bool allzeros = true; + for (size_t i = 0; i < signingPrivateKeySize; i++) + if (m_SigningPrivateKey[i]) { + allzeros = false; + break; + } + if (allzeros) { + // offline information + const uint8_t *offlineInfo = buf + ret; + ret += 4; // expires timestamp + SigningKeyType keyType = bufbe16toh(buf + ret); + ret += 2; // key type + std::unique_ptr transientVerifier(IdentityEx::CreateVerifier(keyType)); + if (!transientVerifier) return 0; + auto keyLen = transientVerifier->GetPublicKeyLen(); + if (keyLen + ret > len) return 0; + transientVerifier->SetPublicKey(buf + ret); + ret += keyLen; + if (m_Public->GetSignatureLen() + ret > len) return 0; + if (!m_Public->Verify(offlineInfo, keyLen + 6, buf + ret)) { + LogPrint(eLogError, "Identity: Offline signature verification failed"); + return 0; + } + ret += m_Public->GetSignatureLen(); + m_TransientSignatureLen = transientVerifier->GetSignatureLen(); + // copy offline signature + size_t offlineInfoLen = buf + ret - offlineInfo; + m_OfflineSignature.resize(offlineInfoLen); + memcpy(m_OfflineSignature.data(), offlineInfo, offlineInfoLen); + // override signing private key + m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen(); + if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0; + memcpy(m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen); + ret += m_TransientSigningPrivateKeyLen; + CreateSigner(keyType); + } else + CreateSigner(m_Public->GetSigningKeyType()); + return ret; + } - size_t PrivateKeys::FromBase64(const std::string& s) - { - uint8_t * buf = new uint8_t[s.length ()]; - size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ()); - size_t ret = FromBuffer (buf, l); - delete[] buf; - return ret; - } + size_t PrivateKeys::ToBuffer(uint8_t *buf, size_t len) const { + size_t ret = m_Public->ToBuffer(buf, len); + auto cryptoKeyLen = GetPrivateKeyLen(); + memcpy(buf + ret, m_PrivateKey, cryptoKeyLen); + ret += cryptoKeyLen; + size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen(); + if (ret + signingPrivateKeySize > len) return 0; // overflow + if (IsOfflineSignature()) + memset(buf + ret, 0, signingPrivateKeySize); + else + memcpy(buf + ret, m_SigningPrivateKey, signingPrivateKeySize); + ret += signingPrivateKeySize; + if (IsOfflineSignature()) { + // offline signature + auto offlineSignatureLen = m_OfflineSignature.size(); + if (ret + offlineSignatureLen > len) return 0; + memcpy(buf + ret, m_OfflineSignature.data(), offlineSignatureLen); + ret += offlineSignatureLen; + // transient private key + if (ret + m_TransientSigningPrivateKeyLen > len) return 0; + memcpy(buf + ret, m_SigningPrivateKey, m_TransientSigningPrivateKeyLen); + ret += m_TransientSigningPrivateKeyLen; + } + return ret; + } - std::string PrivateKeys::ToBase64 () const - { - uint8_t * buf = new uint8_t[GetFullLen ()]; - char * str = new char[GetFullLen ()*2]; - size_t l = ToBuffer (buf, GetFullLen ()); - size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2); - str[l1] = 0; - delete[] buf; - std::string ret(str); - delete[] str; - return ret; - } + size_t PrivateKeys::FromBase64(const std::string &s) { + uint8_t *buf = new uint8_t[s.length()]; + size_t l = i2p::data::Base64ToByteStream(s.c_str(), s.length(), buf, s.length()); + size_t ret = FromBuffer(buf, l); + delete[] buf; + return ret; + } - void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - if (!m_Signer) - CreateSigner(); - m_Signer->Sign (buf, len, signature); - } + std::string PrivateKeys::ToBase64() const { + uint8_t *buf = new uint8_t[GetFullLen()]; + char *str = new char[GetFullLen() * 2]; + size_t l = ToBuffer(buf, GetFullLen()); + size_t l1 = i2p::data::ByteStreamToBase64(buf, l, str, GetFullLen() * 2); + str[l1] = 0; + delete[] buf; + std::string ret(str); + delete[] str; + return ret; + } - void PrivateKeys::CreateSigner () const - { - if (IsOfflineSignature ()) - CreateSigner (bufbe16toh (m_OfflineSignature.data () + 4)); // key type - else - CreateSigner (m_Public->GetSigningKeyType ()); - } + void PrivateKeys::Sign(const uint8_t *buf, int len, uint8_t *signature) const { + if (!m_Signer) + CreateSigner(); + m_Signer->Sign(buf, len, signature); + } - void PrivateKeys::CreateSigner (SigningKeyType keyType) const - { - if (m_Signer) return; - if (keyType == SIGNING_KEY_TYPE_DSA_SHA1) - m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey)); - else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature ()) - m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check - else - { - // public key is not required - auto signer = CreateSigner (keyType, m_SigningPrivateKey); - if (signer) m_Signer.reset (signer); - } - } + void PrivateKeys::CreateSigner() const { + if (IsOfflineSignature()) + CreateSigner(bufbe16toh(m_OfflineSignature.data() + 4)); // key type + else + CreateSigner(m_Public->GetSigningKeyType()); + } - i2p::crypto::Signer * PrivateKeys::CreateSigner (SigningKeyType keyType, const uint8_t * priv) - { - switch (keyType) - { - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - return new i2p::crypto::ECDSAP256Signer (priv); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - return new i2p::crypto::ECDSAP384Signer (priv); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - return new i2p::crypto::ECDSAP521Signer (priv); - break; - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - LogPrint (eLogError, "Identity: RSA signing key type ", (int)keyType, " is not supported"); - break; - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - return new i2p::crypto::EDDSA25519Signer (priv, nullptr); - break; - case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: - return new i2p::crypto::GOSTR3410_256_Signer (i2p::crypto::eGOSTR3410CryptoProA, priv); - break; - case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: - return new i2p::crypto::GOSTR3410_512_Signer (i2p::crypto::eGOSTR3410TC26A512, priv); - break; - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - return new i2p::crypto::RedDSA25519Signer (priv); - break; - default: - LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported"); - } - return nullptr; - } + void PrivateKeys::CreateSigner(SigningKeyType keyType) const { + if (m_Signer) return; + if (keyType == SIGNING_KEY_TYPE_DSA_SHA1) + m_Signer.reset( + new i2p::crypto::DSASigner(m_SigningPrivateKey, m_Public->GetStandardIdentity().signingKey)); + else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature()) + m_Signer.reset(new i2p::crypto::EDDSA25519Signer(m_SigningPrivateKey, + m_Public->GetStandardIdentity().certificate - + i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check + else { + // public key is not required + auto signer = CreateSigner(keyType, m_SigningPrivateKey); + if (signer) m_Signer.reset(signer); + } + } - size_t PrivateKeys::GetSignatureLen () const - { - return IsOfflineSignature () ? m_TransientSignatureLen : m_Public->GetSignatureLen (); - } + i2p::crypto::Signer *PrivateKeys::CreateSigner(SigningKeyType keyType, const uint8_t *priv) { + switch (keyType) { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + return new i2p::crypto::ECDSAP256Signer(priv); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + return new i2p::crypto::ECDSAP384Signer(priv); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + return new i2p::crypto::ECDSAP521Signer(priv); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + LogPrint(eLogError, "Identity: RSA signing key type ", (int) keyType, " is not supported"); + break; + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + return new i2p::crypto::EDDSA25519Signer(priv, nullptr); + break; + case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: + return new i2p::crypto::GOSTR3410_256_Signer(i2p::crypto::eGOSTR3410CryptoProA, priv); + break; + case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: + return new i2p::crypto::GOSTR3410_512_Signer(i2p::crypto::eGOSTR3410TC26A512, priv); + break; + case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: + return new i2p::crypto::RedDSA25519Signer(priv); + break; + default: + LogPrint(eLogError, "Identity: Signing key type ", (int) keyType, " is not supported"); + } + return nullptr; + } - size_t PrivateKeys::GetPrivateKeyLen () const - { - // private key length always 256, but type 4 - return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256; - } + size_t PrivateKeys::GetSignatureLen() const { + return IsOfflineSignature() ? m_TransientSignatureLen : m_Public->GetSignatureLen(); + } - uint8_t * PrivateKeys::GetPadding() - { - if(m_Public->GetSigningKeyType () == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) - return m_Public->GetEncryptionPublicKeyBuffer() + 256; - else - return nullptr; // TODO: implement me - } + size_t PrivateKeys::GetPrivateKeyLen() const { + // private key length always 256, but type 4 + return (m_Public->GetCryptoKeyType() == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256; + } - std::shared_ptr PrivateKeys::CreateDecryptor (const uint8_t * key) const - { - if (!key) key = m_PrivateKey; // use privateKey - return CreateDecryptor (m_Public->GetCryptoKeyType (), key); - } + uint8_t *PrivateKeys::GetPadding() { + if (m_Public->GetSigningKeyType() == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) + return m_Public->GetEncryptionPublicKeyBuffer() + 256; + else + return nullptr; // TODO: implement me + } - std::shared_ptr PrivateKeys::CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key) - { - if (!key) return nullptr; - switch (cryptoType) - { - case CRYPTO_KEY_TYPE_ELGAMAL: - return std::make_shared(key); - break; - case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: - return std::make_shared(key); - break; - case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: - case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: - return std::make_shared(key); - break; - case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: - return std::make_shared(key); - break; - default: - LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType); - }; - return nullptr; - } + std::shared_ptr PrivateKeys::CreateDecryptor(const uint8_t *key) const { + if (!key) key = m_PrivateKey; // use privateKey + return CreateDecryptor(m_Public->GetCryptoKeyType(), key); + } - PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType) - { - if (type != SIGNING_KEY_TYPE_DSA_SHA1) - { - PrivateKeys keys; - // signature - uint8_t signingPublicKey[512]; // signing public key is 512 bytes max - GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey); - // encryption - uint8_t publicKey[256]; - GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey); - // identity - keys.m_Public = std::make_shared (publicKey, signingPublicKey, type, cryptoType); + std::shared_ptr + PrivateKeys::CreateDecryptor(CryptoKeyType cryptoType, const uint8_t *key) { + if (!key) return nullptr; + switch (cryptoType) { + case CRYPTO_KEY_TYPE_ELGAMAL: + return std::make_shared(key); + break; + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: + return std::make_shared(key); + break; + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: + return std::make_shared(key); + break; + case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: + return std::make_shared(key); + break; + default: + LogPrint(eLogError, "Identity: Unknown crypto key type ", (int) cryptoType); + }; + return nullptr; + } - keys.CreateSigner (); - return keys; - } - return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 - } + PrivateKeys PrivateKeys::CreateRandomKeys(SigningKeyType type, CryptoKeyType cryptoType) { + if (type != SIGNING_KEY_TYPE_DSA_SHA1) { + PrivateKeys keys; + // signature + uint8_t signingPublicKey[512]; // signing public key is 512 bytes max + GenerateSigningKeyPair(type, keys.m_SigningPrivateKey, signingPublicKey); + // encryption + uint8_t publicKey[256]; + GenerateCryptoKeyPair(cryptoType, keys.m_PrivateKey, publicKey); + // identity + keys.m_Public = std::make_shared(publicKey, signingPublicKey, type, cryptoType); - void PrivateKeys::GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub) - { - switch (type) - { - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - i2p::crypto::CreateECDSAP256RandomKeys (priv, pub); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - i2p::crypto::CreateECDSAP384RandomKeys (priv, pub); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - i2p::crypto::CreateECDSAP521RandomKeys (priv, pub); - break; - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); + keys.CreateSigner(); + return keys; + } + return PrivateKeys(i2p::data::CreateRandomKeys()); // DSA-SHA1 + } + + void PrivateKeys::GenerateSigningKeyPair(SigningKeyType type, uint8_t *priv, uint8_t *pub) { + switch (type) { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + i2p::crypto::CreateECDSAP256RandomKeys(priv, pub); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + i2p::crypto::CreateECDSAP384RandomKeys(priv, pub); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + i2p::crypto::CreateECDSAP521RandomKeys(priv, pub); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + LogPrint(eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; + [[fallthrough]]; #endif - // no break here - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); - break; - case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: - i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410CryptoProA, priv, pub); - break; - case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: - i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub); - break; - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub); - break; - default: - LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); - i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1 - } - } + // no break here + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + i2p::crypto::CreateEDDSA25519RandomKeys(priv, pub); + break; + case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: + i2p::crypto::CreateGOSTR3410RandomKeys(i2p::crypto::eGOSTR3410CryptoProA, priv, pub); + break; + case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: + i2p::crypto::CreateGOSTR3410RandomKeys(i2p::crypto::eGOSTR3410TC26A512, priv, pub); + break; + case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: + i2p::crypto::CreateRedDSA25519RandomKeys(priv, pub); + break; + default: + LogPrint(eLogWarning, "Identity: Signing key type ", (int) type, + " is not supported. Create DSA-SHA1"); + i2p::crypto::CreateDSARandomKeys(priv, pub); // DSA-SHA1 + } + } - void PrivateKeys::GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub) - { - switch (type) - { - case CRYPTO_KEY_TYPE_ELGAMAL: - i2p::crypto::GenerateElGamalKeyPair(priv, pub); - break; - case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: - case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: - i2p::crypto::CreateECIESP256RandomKeys (priv, pub); - break; - case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: - i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub); - break; - case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: - i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub); - break; - default: - LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported"); - } - } + void PrivateKeys::GenerateCryptoKeyPair(CryptoKeyType type, uint8_t *priv, uint8_t *pub) { + switch (type) { + case CRYPTO_KEY_TYPE_ELGAMAL: + i2p::crypto::GenerateElGamalKeyPair(priv, pub); + break; + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: + i2p::crypto::CreateECIESP256RandomKeys(priv, pub); + break; + case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: + i2p::crypto::CreateECIESGOSTR3410RandomKeys(priv, pub); + break; + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: + i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys(priv, pub); + break; + default: + LogPrint(eLogError, "Identity: Crypto key type ", (int) type, " is not supported"); + } + } - PrivateKeys PrivateKeys::CreateOfflineKeys (SigningKeyType type, uint32_t expires) const - { - PrivateKeys keys (*this); - std::unique_ptr verifier (IdentityEx::CreateVerifier (type)); - if (verifier) - { - size_t pubKeyLen = verifier->GetPublicKeyLen (); - keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen (); - keys.m_TransientSignatureLen = verifier->GetSignatureLen (); - keys.m_OfflineSignature.resize (pubKeyLen + m_Public->GetSignatureLen () + 6); - htobe32buf (keys.m_OfflineSignature.data (), expires); // expires - htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type - GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key - Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature - // recreate signer - keys.m_Signer = nullptr; - keys.CreateSigner (type); - } - return keys; - } + PrivateKeys PrivateKeys::CreateOfflineKeys(SigningKeyType type, uint32_t expires) const { + PrivateKeys keys(*this); + std::unique_ptr verifier(IdentityEx::CreateVerifier(type)); + if (verifier) { + size_t pubKeyLen = verifier->GetPublicKeyLen(); + keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen(); + keys.m_TransientSignatureLen = verifier->GetSignatureLen(); + keys.m_OfflineSignature.resize(pubKeyLen + m_Public->GetSignatureLen() + 6); + htobe32buf(keys.m_OfflineSignature.data(), expires); // expires + htobe16buf(keys.m_OfflineSignature.data() + 4, type); // type + GenerateSigningKeyPair(type, keys.m_SigningPrivateKey, + keys.m_OfflineSignature.data() + 6); // public key + Sign(keys.m_OfflineSignature.data(), pubKeyLen + 6, + keys.m_OfflineSignature.data() + 6 + pubKeyLen); // signature + // recreate signer + keys.m_Signer = nullptr; + keys.CreateSigner(type); + } + return keys; + } - Keys CreateRandomKeys () - { - Keys keys; - // encryption - i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey); - // signing - i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey); - return keys; - } + Keys CreateRandomKeys() { + Keys keys; + // encryption + i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey); + // signing + i2p::crypto::CreateDSARandomKeys(keys.signingPrivateKey, keys.signingKey); + return keys; + } - IdentHash CreateRoutingKey (const IdentHash& ident) - { - uint8_t buf[41]; // ident + yyyymmdd - memcpy (buf, (const uint8_t *)ident, 32); - i2p::util::GetCurrentDate ((char *)(buf + 32)); - IdentHash key; - SHA256(buf, 40, key); - return key; - } + IdentHash CreateRoutingKey(const IdentHash &ident) { + uint8_t buf[41]; // ident + yyyymmdd + memcpy(buf, (const uint8_t *) ident, 32); + i2p::util::GetCurrentDate((char *) (buf + 32)); + IdentHash key; + SHA256(buf, 40, key); + return key; + } - XORMetric operator^(const IdentHash& key1, const IdentHash& key2) - { - XORMetric m; + XORMetric operator^(const IdentHash &key1, const IdentHash &key2) { + XORMetric m; #if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600) - if(i2p::cpu::avx) - { - __asm__ - ( - "vmovups %1, %%ymm0 \n" - "vmovups %2, %%ymm1 \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, %0 \n" - : "=m"(*m.metric) - : "m"(*key1), "m"(*key2) - : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler - ); - } - else + if(i2p::cpu::avx) + { + __asm__ + ( + "vmovups %1, %%ymm0 \n" + "vmovups %2, %%ymm1 \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, %0 \n" + : "=m"(*m.metric) + : "m"(*key1), "m"(*key2) + : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler + ); + } + else #endif - { - const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); - m.metric_ll[0] = hash1[0] ^ hash2[0]; - m.metric_ll[1] = hash1[1] ^ hash2[1]; - m.metric_ll[2] = hash1[2] ^ hash2[2]; - m.metric_ll[3] = hash1[3] ^ hash2[3]; - } + { + const uint64_t *hash1 = key1.GetLL(), *hash2 = key2.GetLL(); + m.metric_ll[0] = hash1[0] ^ hash2[0]; + m.metric_ll[1] = hash1[1] ^ hash2[1]; + m.metric_ll[2] = hash1[2] ^ hash2[2]; + m.metric_ll[3] = hash1[3] ^ hash2[3]; + } - return m; - } -} + return m; + } + } } diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index 10f1d5ed..29c4b463 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -20,229 +20,288 @@ #include "Signature.h" #include "CryptoKey.h" -namespace i2p -{ -namespace data -{ - typedef Tag<32> IdentHash; - inline std::string GetIdentHashAbbreviation (const IdentHash& ident) - { - return ident.ToBase64 ().substr (0, 4); - } +namespace i2p { + namespace data { + typedef Tag<32> IdentHash; - struct Keys - { - uint8_t privateKey[256]; - uint8_t signingPrivateKey[20]; - uint8_t publicKey[256]; - uint8_t signingKey[128]; - }; + inline std::string GetIdentHashAbbreviation(const IdentHash &ident) { + return ident.ToBase64().substr(0, 4); + } - const uint8_t CERTIFICATE_TYPE_NULL = 0; - const uint8_t CERTIFICATE_TYPE_HASHCASH = 1; - const uint8_t CERTIFICATE_TYPE_HIDDEN = 2; - const uint8_t CERTIFICATE_TYPE_SIGNED = 3; - const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; - const uint8_t CERTIFICATE_TYPE_KEY = 5; + struct Keys { + uint8_t privateKey[256]; + uint8_t signingPrivateKey[20]; + uint8_t publicKey[256]; + uint8_t signingKey[128]; + }; - struct Identity - { - uint8_t publicKey[256]; - uint8_t signingKey[128]; - uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length + const uint8_t CERTIFICATE_TYPE_NULL = 0; + const uint8_t CERTIFICATE_TYPE_HASHCASH = 1; + const uint8_t CERTIFICATE_TYPE_HIDDEN = 2; + const uint8_t CERTIFICATE_TYPE_SIGNED = 3; + const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; + const uint8_t CERTIFICATE_TYPE_KEY = 5; - Identity () = default; - Identity (const Keys& keys) { *this = keys; }; - Identity& operator=(const Keys& keys); - size_t FromBuffer (const uint8_t * buf, size_t len); - IdentHash Hash () const; - }; + struct Identity { + uint8_t publicKey[256]; + uint8_t signingKey[128]; + uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length - 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; - 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 + Identity &operator=(const Keys &keys); - const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; - 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 + size_t FromBuffer(const uint8_t *buf, size_t len); - typedef uint16_t SigningKeyType; - typedef uint16_t CryptoKeyType; + IdentHash Hash() const; + }; - const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 - class IdentityEx - { - public: + Keys CreateRandomKeys(); - IdentityEx (); - 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); + const size_t DEFAULT_IDENTITY_SIZE = sizeof(Identity); // 387 bytes - 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; - const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; + const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; + const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1; + const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 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 IdentHash& GetIdentHash () const { return m_IdentHash; }; - const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; }; - uint8_t * GetEncryptionPublicKeyBuffer () { return m_StandardIdentity.publicKey; }; - std::shared_ptr CreateEncryptor (const uint8_t * key) const; - size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; - size_t GetSigningPublicKeyLen () const; - const uint8_t * GetSigningPublicKeyBuffer () const; // returns NULL for P521 - size_t GetSigningPrivateKeyLen () const; - size_t GetSignatureLen () const; - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - SigningKeyType GetSigningKeyType () const; - bool IsRSA () const; // signing key type - CryptoKeyType GetCryptoKeyType () const; - void DropVerifier () const; // to save memory + const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; + 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 - bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); } - void RecalculateIdentHash(uint8_t * buff=nullptr); + typedef uint16_t SigningKeyType; + typedef uint16_t CryptoKeyType; - static i2p::crypto::Verifier * CreateVerifier (SigningKeyType keyType); - static std::shared_ptr CreateEncryptor (CryptoKeyType keyType, const uint8_t * key); + const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 + class IdentityEx { + public: - private: + IdentityEx(); - void CreateVerifier () const; - void UpdateVerifier (i2p::crypto::Verifier * verifier) const; + IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey, + 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; - 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]; - }; + IdentityEx(const IdentityEx &other); - class PrivateKeys // for eepsites - { - public: + IdentityEx(const Identity &standard); - 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; + ~IdentityEx(); - std::shared_ptr 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; + IdentityEx &operator=(const IdentityEx &other); - size_t GetFullLen () const; - size_t FromBuffer (const uint8_t * buf, size_t len); - size_t ToBuffer (uint8_t * buf, size_t len) const; + IdentityEx &operator=(const Identity &standard); - size_t FromBase64(const std::string& s); - std::string ToBase64 () const; + size_t FromBuffer(const uint8_t *buf, size_t len); - std::shared_ptr CreateDecryptor (const uint8_t * key) const; + size_t ToBuffer(uint8_t *buf, size_t len) const; - static std::shared_ptr CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key); - static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL); - static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub); - static void 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); + size_t FromBase64(const std::string &s); - // offline keys - PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; - const std::vector& GetOfflineSignature () const { return m_OfflineSignature; }; + std::string ToBase64() const; - private: + const Identity &GetStandardIdentity() const { return m_StandardIdentity; }; - void CreateSigner () const; - void CreateSigner (SigningKeyType keyType) const; - size_t GetPrivateKeyLen () const; + const IdentHash &GetIdentHash() const { return m_IdentHash; }; - private: + const uint8_t *GetEncryptionPublicKey() const { return m_StandardIdentity.publicKey; }; - std::shared_ptr m_Public; - uint8_t m_PrivateKey[256]; - uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes - mutable std::unique_ptr m_Signer; - std::vector m_OfflineSignature; // non zero length, if applicable - size_t m_TransientSignatureLen = 0; - size_t m_TransientSigningPrivateKeyLen = 0; - }; + uint8_t *GetEncryptionPublicKeyBuffer() { return m_StandardIdentity.publicKey; }; - // kademlia - struct XORMetric - { - union - { - uint8_t metric[32]; - uint64_t metric_ll[4]; - }; + std::shared_ptr CreateEncryptor(const uint8_t *key) const; - 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; }; - }; + size_t GetFullLen() const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; - IdentHash CreateRoutingKey (const IdentHash& ident); - XORMetric operator^(const IdentHash& key1, const IdentHash& key2); + size_t GetSigningPublicKeyLen() const; - // destination for delivery instructions - class RoutingDestination - { - public: + const uint8_t *GetSigningPublicKeyBuffer() const; // returns NULL for P521 + size_t GetSigningPrivateKeyLen() const; - RoutingDestination () {}; - virtual ~RoutingDestination () {}; + size_t GetSignatureLen() const; - virtual std::shared_ptr 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 + bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const; - const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; - virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2 - }; + SigningKeyType GetSigningKeyType() const; - class LocalDestination - { - public: + bool IsRSA() const; // signing key type + CryptoKeyType GetCryptoKeyType() const; - virtual ~LocalDestination() {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0; - virtual std::shared_ptr GetIdentity () const = 0; + void DropVerifier() const; // to save memory - 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 - }; -} + bool operator==(const IdentityEx &other) const { return GetIdentHash() == other.GetIdentHash(); } + + void RecalculateIdentHash(uint8_t *buff = nullptr); + + static i2p::crypto::Verifier *CreateVerifier(SigningKeyType keyType); + + static std::shared_ptr + 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 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 CreateDecryptor(const uint8_t *key) const; + + static std::shared_ptr + CreateDecryptor(CryptoKeyType cryptoType, const uint8_t *key); + + static PrivateKeys CreateRandomKeys(SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, + CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL); + + static void GenerateSigningKeyPair(SigningKeyType type, uint8_t *priv, uint8_t *pub); + + static void + 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 &GetOfflineSignature() const { return m_OfflineSignature; }; + + private: + + void CreateSigner() const; + + void CreateSigner(SigningKeyType keyType) const; + + size_t GetPrivateKeyLen() const; + + private: + + std::shared_ptr m_Public; + uint8_t m_PrivateKey[256]; + uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes + mutable std::unique_ptr m_Signer; + std::vector 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 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 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 diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index 387527e3..a127d617 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -16,1019 +16,982 @@ #include "Tunnel.h" #include "LeaseSet.h" -namespace i2p -{ -namespace data -{ - LeaseSet::LeaseSet (bool storeLeases): - m_IsValid (false), m_StoreLeases (storeLeases), m_ExpirationTime (0), m_EncryptionKey (nullptr), - m_Buffer (nullptr), m_BufferLen (0) - { - } +namespace i2p { + namespace data { + LeaseSet::LeaseSet(bool storeLeases) : + m_IsValid(false), m_StoreLeases(storeLeases), m_ExpirationTime(0), m_EncryptionKey(nullptr), + m_Buffer(nullptr), m_BufferLen(0) { + } - LeaseSet::LeaseSet (const uint8_t * buf, size_t len, bool storeLeases): - m_IsValid (true), m_StoreLeases (storeLeases), m_ExpirationTime (0), m_EncryptionKey (nullptr) - { - m_Buffer = new uint8_t[len]; - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - ReadFromBuffer (); - } + LeaseSet::LeaseSet(const uint8_t *buf, size_t len, bool storeLeases) : + m_IsValid(true), m_StoreLeases(storeLeases), m_ExpirationTime(0), m_EncryptionKey(nullptr) { + m_Buffer = new uint8_t[len]; + memcpy(m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer(); + } - void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature) - { - if (len > m_BufferLen) - { - auto oldBuffer = m_Buffer; - m_Buffer = new uint8_t[len]; - delete[] oldBuffer; - } - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - ReadFromBuffer (false, verifySignature); - } + void LeaseSet::Update(const uint8_t *buf, size_t len, bool verifySignature) { + if (len > m_BufferLen) { + auto oldBuffer = m_Buffer; + m_Buffer = new uint8_t[len]; + delete[] oldBuffer; + } + memcpy(m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer(false, verifySignature); + } - void LeaseSet::PopulateLeases () - { - m_StoreLeases = true; - ReadFromBuffer (false); - } + void LeaseSet::PopulateLeases() { + m_StoreLeases = true; + ReadFromBuffer(false); + } - void LeaseSet::ReadFromBuffer (bool readIdentity, bool verifySignature) - { - if (readIdentity || !m_Identity) - m_Identity = std::make_shared(m_Buffer, m_BufferLen); - size_t size = m_Identity->GetFullLen (); - if (size + 256 > m_BufferLen) - { - LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen)); - m_IsValid = false; - return; - } - if (m_StoreLeases) - { - if (!m_EncryptionKey) m_EncryptionKey = new uint8_t[256]; - memcpy (m_EncryptionKey, m_Buffer + size, 256); - } - size += 256; // encryption key - size += m_Identity->GetSigningPublicKeyLen (); // unused signing key - if (size + 1 > m_BufferLen) - { - LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen)); - m_IsValid = false; - return; - } - uint8_t num = m_Buffer[size]; - size++; // num - LogPrint (eLogDebug, "LeaseSet: Read num=", (int)num); - if (!num || num > MAX_NUM_LEASES) - { - LogPrint (eLogError, "LeaseSet: Rncorrect number of leases", (int)num); - m_IsValid = false; - return; - } - if (size + num*LEASE_SIZE > m_BufferLen) - { - LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen)); - m_IsValid = false; - return; - } + void LeaseSet::ReadFromBuffer(bool readIdentity, bool verifySignature) { + if (readIdentity || !m_Identity) + m_Identity = std::make_shared(m_Buffer, m_BufferLen); + size_t size = m_Identity->GetFullLen(); + if (size + 256 > m_BufferLen) { + LogPrint(eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen)); + m_IsValid = false; + return; + } + if (m_StoreLeases) { + if (!m_EncryptionKey) m_EncryptionKey = new uint8_t[256]; + memcpy(m_EncryptionKey, m_Buffer + size, 256); + } + size += 256; // encryption key + size += m_Identity->GetSigningPublicKeyLen(); // unused signing key + if (size + 1 > m_BufferLen) { + LogPrint(eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen)); + m_IsValid = false; + return; + } + uint8_t num = m_Buffer[size]; + size++; // num + LogPrint(eLogDebug, "LeaseSet: Read num=", (int) num); + if (!num || num > MAX_NUM_LEASES) { + LogPrint(eLogError, "LeaseSet: Rncorrect number of leases", (int) num); + m_IsValid = false; + return; + } + if (size + num * LEASE_SIZE > m_BufferLen) { + LogPrint(eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen)); + m_IsValid = false; + return; + } - UpdateLeasesBegin (); - // process leases - m_ExpirationTime = 0; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - const uint8_t * leases = m_Buffer + size; - for (int i = 0; i < num; i++) - { - Lease lease; - lease.tunnelGateway = leases; - leases += 32; // gateway - lease.tunnelID = bufbe32toh (leases); - leases += 4; // tunnel ID - lease.endDate = bufbe64toh (leases); - leases += 8; // end date - UpdateLease (lease, ts); - } - if (!m_ExpirationTime) - { - LogPrint (eLogWarning, "LeaseSet: All leases are expired. Dropped"); - m_IsValid = false; - return; - } - m_ExpirationTime += LEASE_ENDDATE_THRESHOLD; - UpdateLeasesEnd (); + UpdateLeasesBegin(); + // process leases + m_ExpirationTime = 0; + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + const uint8_t *leases = m_Buffer + size; + for (int i = 0; i < num; i++) { + Lease lease; + lease.tunnelGateway = leases; + leases += 32; // gateway + lease.tunnelID = bufbe32toh(leases); + leases += 4; // tunnel ID + lease.endDate = bufbe64toh(leases); + leases += 8; // end date + UpdateLease(lease, ts); + } + if (!m_ExpirationTime) { + LogPrint(eLogWarning, "LeaseSet: All leases are expired. Dropped"); + m_IsValid = false; + return; + } + m_ExpirationTime += LEASE_ENDDATE_THRESHOLD; + UpdateLeasesEnd(); - // verify - if (verifySignature) - { - auto signedSize = leases - m_Buffer; - if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen) - { - LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen)); - m_IsValid = false; - } - else if (!m_Identity->Verify (m_Buffer, signedSize, leases)) - { - LogPrint (eLogWarning, "LeaseSet: Verification failed"); - m_IsValid = false; - } - } - } + // verify + if (verifySignature) { + auto signedSize = leases - m_Buffer; + if (signedSize + m_Identity->GetSignatureLen() > m_BufferLen) { + LogPrint(eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen)); + m_IsValid = false; + } else if (!m_Identity->Verify(m_Buffer, signedSize, leases)) { + LogPrint(eLogWarning, "LeaseSet: Verification failed"); + m_IsValid = false; + } + } + } - void LeaseSet::UpdateLeasesBegin () - { - // reset existing leases - if (m_StoreLeases) - for (auto& it: m_Leases) - it->isUpdated = false; - else - m_Leases.clear (); - } + void LeaseSet::UpdateLeasesBegin() { + // reset existing leases + if (m_StoreLeases) + for (auto &it: m_Leases) + it->isUpdated = false; + else + m_Leases.clear(); + } - void LeaseSet::UpdateLeasesEnd () - { - // delete old leases - if (m_StoreLeases) - { - for (auto it = m_Leases.begin (); it != m_Leases.end ();) - { - if (!(*it)->isUpdated) - { - (*it)->endDate = 0; // somebody might still hold it - m_Leases.erase (it++); - } - else - ++it; - } - } - } + void LeaseSet::UpdateLeasesEnd() { + // delete old leases + if (m_StoreLeases) { + for (auto it = m_Leases.begin(); it != m_Leases.end();) { + if (!(*it)->isUpdated) { + (*it)->endDate = 0; // somebody might still hold it + m_Leases.erase(it++); + } else + ++it; + } + } + } - void LeaseSet::UpdateLease (const Lease& lease, uint64_t ts) - { - if (ts < lease.endDate + LEASE_ENDDATE_THRESHOLD) - { - if (lease.endDate > m_ExpirationTime) - m_ExpirationTime = lease.endDate; - if (m_StoreLeases) - { - auto ret = m_Leases.insert (i2p::data::netdb.NewLease (lease)); - if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing - (*ret.first)->isUpdated = true; - } - } - else - LogPrint (eLogWarning, "LeaseSet: Lease is expired already"); - } + void LeaseSet::UpdateLease(const Lease &lease, uint64_t ts) { + if (ts < lease.endDate + LEASE_ENDDATE_THRESHOLD) { + if (lease.endDate > m_ExpirationTime) + m_ExpirationTime = lease.endDate; + if (m_StoreLeases) { + auto ret = m_Leases.insert(i2p::data::netdb.NewLease(lease)); + if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing + (*ret.first)->isUpdated = true; + } + } else + LogPrint(eLogWarning, "LeaseSet: Lease is expired already"); + } - uint64_t LeaseSet::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const - { - if (!m_Identity) return 0; - size_t size = m_Identity->GetFullLen (); - if (size > len) return 0; - size += 256; // encryption key - size += m_Identity->GetSigningPublicKeyLen (); // unused signing key - if (size > len) return 0; - uint8_t num = buf[size]; - size++; // num - if (size + num*LEASE_SIZE > len) return 0; - uint64_t timestamp= 0 ; - for (int i = 0; i < num; i++) - { - size += 36; // gateway (32) + tunnelId(4) - auto endDate = bufbe64toh (buf + size); - size += 8; // end date - if (!timestamp || endDate < timestamp) - timestamp = endDate; - } - return timestamp; - } + uint64_t LeaseSet::ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const { + if (!m_Identity) return 0; + size_t size = m_Identity->GetFullLen(); + if (size > len) return 0; + size += 256; // encryption key + size += m_Identity->GetSigningPublicKeyLen(); // unused signing key + if (size > len) return 0; + uint8_t num = buf[size]; + size++; // num + if (size + num * LEASE_SIZE > len) return 0; + uint64_t timestamp = 0; + for (int i = 0; i < num; i++) { + size += 36; // gateway (32) + tunnelId(4) + auto endDate = bufbe64toh(buf + size); + size += 8; // end date + if (!timestamp || endDate < timestamp) + timestamp = endDate; + } + return timestamp; + } - bool LeaseSet::IsNewer (const uint8_t * buf, size_t len) const - { - return ExtractExpirationTimestamp (buf, len) > ExtractExpirationTimestamp (m_Buffer, m_BufferLen); - } + bool LeaseSet::IsNewer(const uint8_t *buf, size_t len) const { + return ExtractExpirationTimestamp(buf, len) > ExtractExpirationTimestamp(m_Buffer, m_BufferLen); + } - bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const - { - auto now = i2p::util::GetMillisecondsSinceEpoch (); - if (fudge) now += rand() % fudge; - if (now >= m_ExpirationTime) return true; - return m_ExpirationTime - now <= dlt; - } + bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const { + auto now = i2p::util::GetMillisecondsSinceEpoch(); + if (fudge) now += rand() % fudge; + if (now >= m_ExpirationTime) return true; + return m_ExpirationTime - now <= dlt; + } - const std::vector > LeaseSet::GetNonExpiredLeases (bool withThreshold) const - { - return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold); - } + const std::vector > LeaseSet::GetNonExpiredLeases(bool withThreshold) const { + return GetNonExpiredLeasesExcluding([](const Lease &l) -> bool { return false; }, withThreshold); + } - const std::vector > LeaseSet::GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold) const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector > leases; - for (const auto& it: m_Leases) - { - auto endDate = it->endDate; - if (withThreshold) - endDate += LEASE_ENDDATE_THRESHOLD; - else - endDate -= LEASE_ENDDATE_THRESHOLD; - if (ts < endDate && !exclude(*it)) - leases.push_back (it); - } - return leases; - } + const std::vector > + LeaseSet::GetNonExpiredLeasesExcluding(LeaseInspectFunc exclude, bool withThreshold) const { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + std::vector > leases; + for (const auto &it: m_Leases) { + auto endDate = it->endDate; + if (withThreshold) + endDate += LEASE_ENDDATE_THRESHOLD; + else + endDate -= LEASE_ENDDATE_THRESHOLD; + if (ts < endDate && !exclude(*it)) + leases.push_back(it); + } + return leases; + } - bool LeaseSet::HasExpiredLeases () const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (const auto& it: m_Leases) - if (ts >= it->endDate) return true; - return false; - } + bool LeaseSet::HasExpiredLeases() const { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + for (const auto &it: m_Leases) + if (ts >= it->endDate) return true; + return false; + } - bool LeaseSet::IsExpired () const - { - if (m_StoreLeases && IsEmpty ()) return true; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - return ts > m_ExpirationTime; - } + bool LeaseSet::IsExpired() const { + if (m_StoreLeases && IsEmpty()) return true; + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + return ts > m_ExpirationTime; + } - void LeaseSet::Encrypt (const uint8_t * data, uint8_t * encrypted) const - { - if (!m_EncryptionKey) return; - auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey); - if (encryptor) - encryptor->Encrypt (data, encrypted); - } + void LeaseSet::Encrypt(const uint8_t *data, uint8_t *encrypted) const { + if (!m_EncryptionKey) return; + auto encryptor = m_Identity->CreateEncryptor(m_EncryptionKey); + if (encryptor) + encryptor->Encrypt(data, encrypted); + } - void LeaseSet::SetBuffer (const uint8_t * buf, size_t len) - { - if (m_Buffer) delete[] m_Buffer; - m_Buffer = new uint8_t[len]; - m_BufferLen = len; - memcpy (m_Buffer, buf, len); - } + void LeaseSet::SetBuffer(const uint8_t *buf, size_t len) { + if (m_Buffer) delete[] m_Buffer; + m_Buffer = new uint8_t[len]; + m_BufferLen = len; + memcpy(m_Buffer, buf, len); + } - void LeaseSet::SetBufferLen (size_t len) - { - if (len <= m_BufferLen) m_BufferLen = len; - else - LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen)); - } + void LeaseSet::SetBufferLen(size_t len) { + if (len <= m_BufferLen) m_BufferLen = len; + else + LogPrint(eLogError, "LeaseSet2: Actual buffer size ", int(len), " exceeds full buffer size ", + int(m_BufferLen)); + } - LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): - LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto) - { - SetBuffer (buf, len); - if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) - ReadFromBufferEncrypted (buf, len, nullptr, nullptr); - else - ReadFromBuffer (buf, len); - } + LeaseSet2::LeaseSet2(uint8_t storeType, const uint8_t *buf, size_t len, bool storeLeases, + CryptoKeyType preferredCrypto) : + LeaseSet(storeLeases), m_StoreType(storeType), m_EncryptionType(preferredCrypto) { + SetBuffer(buf, len); + if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) + ReadFromBufferEncrypted(buf, len, nullptr, nullptr); + else + ReadFromBuffer(buf, len); + } - LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, - const uint8_t * secret, CryptoKeyType preferredCrypto): - LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto) - { - ReadFromBufferEncrypted (buf, len, key, secret); - } + LeaseSet2::LeaseSet2(const uint8_t *buf, size_t len, std::shared_ptr key, + const uint8_t *secret, CryptoKeyType preferredCrypto) : + LeaseSet(true), m_StoreType(NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType(preferredCrypto) { + ReadFromBufferEncrypted(buf, len, key, secret); + } - void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature) - { - SetBuffer (buf, len); - if (GetStoreType () != NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) - ReadFromBuffer (buf, len, false, verifySignature); - // TODO: implement encrypted - } + void LeaseSet2::Update(const uint8_t *buf, size_t len, bool verifySignature) { + SetBuffer(buf, len); + if (GetStoreType() != NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) + ReadFromBuffer(buf, len, false, verifySignature); + // TODO: implement encrypted + } - bool LeaseSet2::IsNewer (const uint8_t * buf, size_t len) const - { - uint64_t expiration; - return ExtractPublishedTimestamp (buf, len, expiration) > m_PublishedTimestamp; - } + bool LeaseSet2::IsNewer(const uint8_t *buf, size_t len) const { + uint64_t expiration; + return ExtractPublishedTimestamp(buf, len, expiration) > m_PublishedTimestamp; + } - void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity, bool verifySignature) - { - // standard LS2 header - std::shared_ptr identity; - if (readIdentity) - { - identity = std::make_shared(buf, len); - SetIdentity (identity); - } - else - identity = GetIdentity (); - size_t offset = identity->GetFullLen (); - if (offset + 8 > len) return; - m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds) - uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds) - SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds - uint16_t flags = bufbe16toh (buf + offset); offset += 2; // flags - if (flags & LEASESET2_FLAG_OFFLINE_KEYS) - { - // transient key - m_TransientVerifier = ProcessOfflineSignature (identity, buf, len, offset); - if (!m_TransientVerifier) - { - LogPrint (eLogError, "LeaseSet2: Offline signature failed"); - return; - } - } - if (flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET) m_IsPublic = false; - if (flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED) - { - m_IsPublishedEncrypted = true; - m_IsPublic = true; - } - // type specific part - size_t s = 0; - switch (m_StoreType) - { - case NETDB_STORE_TYPE_STANDARD_LEASESET2: - s = ReadStandardLS2TypeSpecificPart (buf + offset, len - offset); - break; - case NETDB_STORE_TYPE_META_LEASESET2: - s = ReadMetaLS2TypeSpecificPart (buf + offset, len - offset); - break; - default: - LogPrint (eLogWarning, "LeaseSet2: Unexpected store type ", (int)m_StoreType); - } - if (!s) return; - offset += s; - if (verifySignature || m_TransientVerifier) - { - // verify signature - bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) : - VerifySignature (identity, buf, len, offset); - SetIsValid (verified); - } - offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen (); - if (offset > len) { - LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len)); - return; - } - SetBufferLen (offset); - } + void LeaseSet2::ReadFromBuffer(const uint8_t *buf, size_t len, bool readIdentity, bool verifySignature) { + // standard LS2 header + std::shared_ptr identity; + if (readIdentity) { + identity = std::make_shared(buf, len); + SetIdentity(identity); + } else + identity = GetIdentity(); + size_t offset = identity->GetFullLen(); + if (offset + 8 > len) return; + m_PublishedTimestamp = bufbe32toh(buf + offset); + offset += 4; // published timestamp (seconds) + uint16_t expires = bufbe16toh(buf + offset); + offset += 2; // expires (seconds) + SetExpirationTime((m_PublishedTimestamp + expires) * 1000LL); // in milliseconds + uint16_t flags = bufbe16toh(buf + offset); + offset += 2; // flags + if (flags & LEASESET2_FLAG_OFFLINE_KEYS) { + // transient key + m_TransientVerifier = ProcessOfflineSignature(identity, buf, len, offset); + if (!m_TransientVerifier) { + LogPrint(eLogError, "LeaseSet2: Offline signature failed"); + return; + } + } + if (flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET) m_IsPublic = false; + if (flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED) { + m_IsPublishedEncrypted = true; + m_IsPublic = true; + } + // type specific part + size_t s = 0; + switch (m_StoreType) { + case NETDB_STORE_TYPE_STANDARD_LEASESET2: + s = ReadStandardLS2TypeSpecificPart(buf + offset, len - offset); + break; + case NETDB_STORE_TYPE_META_LEASESET2: + s = ReadMetaLS2TypeSpecificPart(buf + offset, len - offset); + break; + default: + LogPrint(eLogWarning, "LeaseSet2: Unexpected store type ", (int) m_StoreType); + } + if (!s) return; + offset += s; + if (verifySignature || m_TransientVerifier) { + // verify signature + bool verified = m_TransientVerifier ? VerifySignature(m_TransientVerifier, buf, len, offset) : + VerifySignature(identity, buf, len, offset); + SetIsValid(verified); + } + offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen() : identity->GetSignatureLen(); + if (offset > len) { + LogPrint(eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len)); + return; + } + SetBufferLen(offset); + } - template - bool LeaseSet2::VerifySignature (Verifier& verifier, const uint8_t * buf, size_t len, size_t signatureOffset) - { - if (signatureOffset + verifier->GetSignatureLen () > len) return false; - // we assume buf inside DatabaseStore message, so buf[-1] is valid memory - // change it for signature verification, and restore back - uint8_t c = buf[-1]; - const_cast(buf)[-1] = m_StoreType; - bool verified = verifier->Verify (buf - 1, signatureOffset + 1, buf + signatureOffset); - const_cast(buf)[-1] = c; - if (!verified) - LogPrint (eLogWarning, "LeaseSet2: Verification failed"); - return verified; - } + template + bool LeaseSet2::VerifySignature(Verifier &verifier, const uint8_t *buf, size_t len, size_t signatureOffset) { + if (signatureOffset + verifier->GetSignatureLen() > len) return false; + // we assume buf inside DatabaseStore message, so buf[-1] is valid memory + // change it for signature verification, and restore back + uint8_t c = buf[-1]; + const_cast(buf)[-1] = m_StoreType; + bool verified = verifier->Verify(buf - 1, signatureOffset + 1, buf + signatureOffset); + const_cast(buf)[-1] = c; + if (!verified) + LogPrint(eLogWarning, "LeaseSet2: Verification failed"); + return verified; + } - size_t LeaseSet2::ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len) - { - size_t offset = 0; - // properties - uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; - offset += propertiesLen; // skip for now. TODO: implement properties - // key sections - CryptoKeyType preferredKeyType = m_EncryptionType; - bool preferredKeyFound = false; - if (offset + 1 > len) return 0; - int numKeySections = buf[offset]; offset++; - for (int i = 0; i < numKeySections; i++) - { - if (offset + 4 > len) return 0; - uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type - uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; - if (offset + encryptionKeyLen > len) return 0; - if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only - { - // we pick first valid key if preferred not found - auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); - if (encryptor && (!m_Encryptor || keyType == preferredKeyType)) - { - m_Encryptor = encryptor; // TODO: atomic - m_EncryptionType = keyType; - if (keyType == preferredKeyType) preferredKeyFound = true; - } - } - offset += encryptionKeyLen; - } - // leases - if (offset + 1 > len) return 0; - int numLeases = buf[offset]; offset++; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (IsStoreLeases ()) - { - UpdateLeasesBegin (); - for (int i = 0; i < numLeases; i++) - { - if (offset + LEASE2_SIZE > len) return 0; - Lease lease; - lease.tunnelGateway = buf + offset; offset += 32; // gateway - lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID - lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date - UpdateLease (lease, ts); - } - UpdateLeasesEnd (); - } - else - offset += numLeases*LEASE2_SIZE; // 40 bytes per lease + size_t LeaseSet2::ReadStandardLS2TypeSpecificPart(const uint8_t *buf, size_t len) { + size_t offset = 0; + // properties + uint16_t propertiesLen = bufbe16toh(buf + offset); + offset += 2; + offset += propertiesLen; // skip for now. TODO: implement properties + // key sections + CryptoKeyType preferredKeyType = m_EncryptionType; + bool preferredKeyFound = false; + if (offset + 1 > len) return 0; + int numKeySections = buf[offset]; + offset++; + for (int i = 0; i < numKeySections; i++) { + if (offset + 4 > len) return 0; + uint16_t keyType = bufbe16toh(buf + offset); + offset += 2; // encryption key type + uint16_t encryptionKeyLen = bufbe16toh(buf + offset); + offset += 2; + if (offset + encryptionKeyLen > len) return 0; + if (IsStoreLeases() && !preferredKeyFound) // create encryptor with leases only + { + // we pick first valid key if preferred not found + auto encryptor = i2p::data::IdentityEx::CreateEncryptor(keyType, buf + offset); + if (encryptor && (!m_Encryptor || keyType == preferredKeyType)) { + m_Encryptor = encryptor; // TODO: atomic + m_EncryptionType = keyType; + if (keyType == preferredKeyType) preferredKeyFound = true; + } + } + offset += encryptionKeyLen; + } + // leases + if (offset + 1 > len) return 0; + int numLeases = buf[offset]; + offset++; + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + if (IsStoreLeases()) { + UpdateLeasesBegin(); + for (int i = 0; i < numLeases; i++) { + if (offset + LEASE2_SIZE > len) return 0; + Lease lease; + lease.tunnelGateway = buf + offset; + offset += 32; // gateway + lease.tunnelID = bufbe32toh(buf + offset); + offset += 4; // tunnel ID + lease.endDate = bufbe32toh(buf + offset) * 1000LL; + offset += 4; // end date + UpdateLease(lease, ts); + } + UpdateLeasesEnd(); + } else + offset += numLeases * LEASE2_SIZE; // 40 bytes per lease - return (offset > len ? 0 : offset); - } + return (offset > len ? 0 : offset); + } - size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len) - { - size_t offset = 0; - // properties - uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; - offset += propertiesLen; // skip for now. TODO: implement properties - // entries - if (offset + 1 > len) return 0; - int numEntries = buf[offset]; offset++; - for (int i = 0; i < numEntries; i++) - { - if (offset + LEASE2_SIZE > len) return 0; - offset += 32; // hash - offset += 3; // flags - offset += 1; // cost - offset += 4; // expires - } - // revocations - if (offset + 1 > len) return 0; - int numRevocations = buf[offset]; offset++; - for (int i = 0; i < numRevocations; i++) - { - if (offset + 32 > len) return 0; - offset += 32; // hash - } - return offset; - } + size_t LeaseSet2::ReadMetaLS2TypeSpecificPart(const uint8_t *buf, size_t len) { + size_t offset = 0; + // properties + uint16_t propertiesLen = bufbe16toh(buf + offset); + offset += 2; + offset += propertiesLen; // skip for now. TODO: implement properties + // entries + if (offset + 1 > len) return 0; + int numEntries = buf[offset]; + offset++; + for (int i = 0; i < numEntries; i++) { + if (offset + LEASE2_SIZE > len) return 0; + offset += 32; // hash + offset += 3; // flags + offset += 1; // cost + offset += 4; // expires + } + // revocations + if (offset + 1 > len) return 0; + int numRevocations = buf[offset]; + offset++; + for (int i = 0; i < numRevocations; i++) { + if (offset + 32 > len) return 0; + offset += 32; // hash + } + return offset; + } - void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret) - { - size_t offset = 0; - // blinded key - if (len < 2) return; - const uint8_t * stA1 = buf + offset; // stA1 = blinded signature type, 2 bytes big endian - uint16_t blindedKeyType = bufbe16toh (stA1); offset += 2; - std::unique_ptr blindedVerifier (i2p::data::IdentityEx::CreateVerifier (blindedKeyType)); - if (!blindedVerifier) return; - auto blindedKeyLen = blindedVerifier->GetPublicKeyLen (); - if (offset + blindedKeyLen >= len) return; - const uint8_t * blindedPublicKey = buf + offset; - blindedVerifier->SetPublicKey (blindedPublicKey); offset += blindedKeyLen; - // expiration - if (offset + 8 >= len) return; - const uint8_t * publishedTimestamp = buf + offset; - m_PublishedTimestamp = bufbe32toh (publishedTimestamp); offset += 4; // published timestamp (seconds) - uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds) - SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds - uint16_t flags = bufbe16toh (buf + offset); offset += 2; // flags - if (flags & LEASESET2_FLAG_OFFLINE_KEYS) - { - // transient key - m_TransientVerifier = ProcessOfflineSignature (blindedVerifier, buf, len, offset); - if (!m_TransientVerifier) - { - LogPrint (eLogError, "LeaseSet2: Offline signature failed"); - return; - } - } - // outer ciphertext - if (offset + 2 > len) return; - uint16_t lenOuterCiphertext = bufbe16toh (buf + offset); offset += 2; - const uint8_t * outerCiphertext = buf + offset; - offset += lenOuterCiphertext; - // verify signature - bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) : - VerifySignature (blindedVerifier, buf, len, offset); - SetIsValid (verified); - // handle ciphertext - if (verified && key && lenOuterCiphertext >= 32) - { - SetIsValid (false); // we must verify it again in Layer 2 - if (blindedKeyType == key->GetBlindedSigType ()) - { - // verify blinding - char date[9]; - i2p::util::GetDateString (m_PublishedTimestamp, date); - std::vector blinded (blindedKeyLen); - key->GetBlindedKey (date, blinded.data ()); - if (memcmp (blindedPublicKey, blinded.data (), blindedKeyLen)) - { - LogPrint (eLogError, "LeaseSet2: Blinded public key doesn't match"); - return; - } - } - else - { - LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instead ", key->GetBlindedSigType ()); - return; - } - // outer key - // outerInput = subcredential || publishedTimestamp - uint8_t subcredential[36]; - key->GetSubcredential (blindedPublicKey, blindedKeyLen, subcredential); - memcpy (subcredential + 32, publishedTimestamp, 4); - // outerSalt = outerCiphertext[0:32] - // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) - uint8_t keys[64]; // 44 bytes actual data - i2p::crypto::HKDF (outerCiphertext, subcredential, 36, "ELS2_L1K", keys); - // decrypt Layer 1 - // outerKey = keys[0:31] - // outerIV = keys[32:43] - size_t lenOuterPlaintext = lenOuterCiphertext - 32; - std::vector outerPlainText (lenOuterPlaintext); - i2p::crypto::ChaCha20 (outerCiphertext + 32, lenOuterPlaintext, keys, keys + 32, outerPlainText.data ()); - // inner key - // innerInput = authCookie || subcredential || publishedTimestamp - // innerSalt = innerCiphertext[0:32] - // keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44) - uint8_t innerInput[68]; - size_t authDataLen = ExtractClientAuthData (outerPlainText.data (), lenOuterPlaintext, secret, subcredential, innerInput); - if (authDataLen > 0) - { - memcpy (innerInput + 32, subcredential, 36); - i2p::crypto::HKDF (outerPlainText.data () + 1 + authDataLen, innerInput, 68, "ELS2_L2K", keys); - } - else - // no authData presented, innerInput = subcredential || publishedTimestamp - // skip 1 byte flags - i2p::crypto::HKDF (outerPlainText.data () + 1, subcredential, 36, "ELS2_L2K", keys); // no authCookie - // decrypt Layer 2 - // innerKey = keys[0:31] - // innerIV = keys[32:43] - size_t lenInnerPlaintext = lenOuterPlaintext - 32 - 1 - authDataLen; - std::vector innerPlainText (lenInnerPlaintext); - i2p::crypto::ChaCha20 (outerPlainText.data () + 32 + 1 + authDataLen, lenInnerPlaintext, keys, keys + 32, innerPlainText.data ()); - if (innerPlainText[0] == NETDB_STORE_TYPE_STANDARD_LEASESET2 || innerPlainText[0] == NETDB_STORE_TYPE_META_LEASESET2) - { - // override store type and buffer - m_StoreType = innerPlainText[0]; - SetBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); - // parse and verify Layer 2 - ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); - } - else - LogPrint (eLogError, "LeaseSet2: Unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet"); - } - else - { - // we set actual length of encrypted buffer - offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : blindedVerifier->GetSignatureLen (); - SetBufferLen (offset); - } - } + void + LeaseSet2::ReadFromBufferEncrypted(const uint8_t *buf, size_t len, std::shared_ptr key, + const uint8_t *secret) { + size_t offset = 0; + // blinded key + if (len < 2) return; + const uint8_t *stA1 = buf + offset; // stA1 = blinded signature type, 2 bytes big endian + uint16_t blindedKeyType = bufbe16toh(stA1); + offset += 2; + std::unique_ptr blindedVerifier( + i2p::data::IdentityEx::CreateVerifier(blindedKeyType)); + if (!blindedVerifier) return; + auto blindedKeyLen = blindedVerifier->GetPublicKeyLen(); + if (offset + blindedKeyLen >= len) return; + const uint8_t *blindedPublicKey = buf + offset; + blindedVerifier->SetPublicKey(blindedPublicKey); + offset += blindedKeyLen; + // expiration + if (offset + 8 >= len) return; + const uint8_t *publishedTimestamp = buf + offset; + m_PublishedTimestamp = bufbe32toh(publishedTimestamp); + offset += 4; // published timestamp (seconds) + uint16_t expires = bufbe16toh(buf + offset); + offset += 2; // expires (seconds) + SetExpirationTime((m_PublishedTimestamp + expires) * 1000LL); // in milliseconds + uint16_t flags = bufbe16toh(buf + offset); + offset += 2; // flags + if (flags & LEASESET2_FLAG_OFFLINE_KEYS) { + // transient key + m_TransientVerifier = ProcessOfflineSignature(blindedVerifier, buf, len, offset); + if (!m_TransientVerifier) { + LogPrint(eLogError, "LeaseSet2: Offline signature failed"); + return; + } + } + // outer ciphertext + if (offset + 2 > len) return; + uint16_t lenOuterCiphertext = bufbe16toh(buf + offset); + offset += 2; + const uint8_t *outerCiphertext = buf + offset; + offset += lenOuterCiphertext; + // verify signature + bool verified = m_TransientVerifier ? VerifySignature(m_TransientVerifier, buf, len, offset) : + VerifySignature(blindedVerifier, buf, len, offset); + SetIsValid(verified); + // handle ciphertext + if (verified && key && lenOuterCiphertext >= 32) { + SetIsValid(false); // we must verify it again in Layer 2 + if (blindedKeyType == key->GetBlindedSigType()) { + // verify blinding + char date[9]; + i2p::util::GetDateString(m_PublishedTimestamp, date); + std::vector blinded(blindedKeyLen); + key->GetBlindedKey(date, blinded.data()); + if (memcmp(blindedPublicKey, blinded.data(), blindedKeyLen)) { + LogPrint(eLogError, "LeaseSet2: Blinded public key doesn't match"); + return; + } + } else { + LogPrint(eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instead ", + key->GetBlindedSigType()); + return; + } + // outer key + // outerInput = subcredential || publishedTimestamp + uint8_t subcredential[36]; + key->GetSubcredential(blindedPublicKey, blindedKeyLen, subcredential); + memcpy(subcredential + 32, publishedTimestamp, 4); + // outerSalt = outerCiphertext[0:32] + // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) + uint8_t keys[64]; // 44 bytes actual data + i2p::crypto::HKDF(outerCiphertext, subcredential, 36, "ELS2_L1K", keys); + // decrypt Layer 1 + // outerKey = keys[0:31] + // outerIV = keys[32:43] + size_t lenOuterPlaintext = lenOuterCiphertext - 32; + std::vector outerPlainText(lenOuterPlaintext); + i2p::crypto::ChaCha20(outerCiphertext + 32, lenOuterPlaintext, keys, keys + 32, outerPlainText.data()); + // inner key + // innerInput = authCookie || subcredential || publishedTimestamp + // innerSalt = innerCiphertext[0:32] + // keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44) + uint8_t innerInput[68]; + size_t authDataLen = ExtractClientAuthData(outerPlainText.data(), lenOuterPlaintext, secret, + subcredential, innerInput); + if (authDataLen > 0) { + memcpy(innerInput + 32, subcredential, 36); + i2p::crypto::HKDF(outerPlainText.data() + 1 + authDataLen, innerInput, 68, "ELS2_L2K", keys); + } else + // no authData presented, innerInput = subcredential || publishedTimestamp + // skip 1 byte flags + i2p::crypto::HKDF(outerPlainText.data() + 1, subcredential, 36, "ELS2_L2K", keys); // no authCookie + // decrypt Layer 2 + // innerKey = keys[0:31] + // innerIV = keys[32:43] + size_t lenInnerPlaintext = lenOuterPlaintext - 32 - 1 - authDataLen; + std::vector innerPlainText(lenInnerPlaintext); + i2p::crypto::ChaCha20(outerPlainText.data() + 32 + 1 + authDataLen, lenInnerPlaintext, keys, keys + 32, + innerPlainText.data()); + if (innerPlainText[0] == NETDB_STORE_TYPE_STANDARD_LEASESET2 || + innerPlainText[0] == NETDB_STORE_TYPE_META_LEASESET2) { + // override store type and buffer + m_StoreType = innerPlainText[0]; + SetBuffer(innerPlainText.data() + 1, lenInnerPlaintext - 1); + // parse and verify Layer 2 + ReadFromBuffer(innerPlainText.data() + 1, lenInnerPlaintext - 1); + } else + LogPrint(eLogError, "LeaseSet2: Unexpected LeaseSet type ", (int) innerPlainText[0], + " inside encrypted LeaseSet"); + } else { + // we set actual length of encrypted buffer + offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen() + : blindedVerifier->GetSignatureLen(); + SetBufferLen(offset); + } + } - // helper for ExtractClientAuthData - static inline bool GetAuthCookie (const uint8_t * authClients, int numClients, const uint8_t * okm, uint8_t * authCookie) - { - // try to find clientCookie_i for clientID_i = okm[44:51] - for (int i = 0; i < numClients; i++) - { - if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i - { - // clientKey_i = okm[0:31] - // clientIV_i = okm[32:43] - i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i - return true; - } - } - return false; - } + // helper for ExtractClientAuthData + static inline bool + GetAuthCookie(const uint8_t *authClients, int numClients, const uint8_t *okm, uint8_t *authCookie) { + // try to find clientCookie_i for clientID_i = okm[44:51] + for (int i = 0; i < numClients; i++) { + if (!memcmp(okm + 44, authClients + i * 40, 8)) // clientID_i + { + // clientKey_i = okm[0:31] + // clientIV_i = okm[32:43] + i2p::crypto::ChaCha20(authClients + i * 40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i + return true; + } + } + return false; + } - size_t LeaseSet2::ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const - { - size_t offset = 0; - uint8_t flag = buf[offset]; offset++; // flag - if (flag & 0x01) // client auth - { - if (!(flag & 0x0E)) // DH, bit 1-3 all zeroes - { - const uint8_t * ephemeralPublicKey = buf + offset; offset += 32; // ephemeralPublicKey - uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients - const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients - if (offset > len) - { - LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data"); - return 0; - } - // calculate authCookie - if (secret) - { - i2p::crypto::X25519Keys ck (secret, nullptr); // derive cpk_i from csk_i - uint8_t authInput[100]; - ck.Agree (ephemeralPublicKey, authInput); // sharedSecret is first 32 bytes of authInput - memcpy (authInput + 32, ck.GetPublicKey (), 32); // cpk_i - memcpy (authInput + 64, subcredential, 36); - uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm); - if (!GetAuthCookie (authClients, numClients, okm, authCookie)) - LogPrint (eLogError, "LeaseSet2: Client cookie DH not found"); - } - else - LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided"); - } - else if (flag & 0x02) // PSK, bit 1 is set to 1 - { - const uint8_t * authSalt = buf + offset; offset += 32; // authSalt - uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients - const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients - if (offset > len) - { - LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data"); - return 0; - } - // calculate authCookie - if (secret) - { - uint8_t authInput[68]; - memcpy (authInput, secret, 32); - memcpy (authInput + 32, subcredential, 36); - uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm); - if (!GetAuthCookie (authClients, numClients, okm, authCookie)) - LogPrint (eLogError, "LeaseSet2: Client cookie PSK not found"); - } - else - LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided"); - } - else - LogPrint (eLogError, "LeaseSet2: Unknown client auth type ", (int)flag); - } - return offset - 1; - } + size_t LeaseSet2::ExtractClientAuthData(const uint8_t *buf, size_t len, const uint8_t *secret, + const uint8_t *subcredential, uint8_t *authCookie) const { + size_t offset = 0; + uint8_t flag = buf[offset]; + offset++; // flag + if (flag & 0x01) // client auth + { + if (!(flag & 0x0E)) // DH, bit 1-3 all zeroes + { + const uint8_t *ephemeralPublicKey = buf + offset; + offset += 32; // ephemeralPublicKey + uint16_t numClients = bufbe16toh(buf + offset); + offset += 2; // clients + const uint8_t *authClients = buf + offset; + offset += numClients * 40; // authClients + if (offset > len) { + LogPrint(eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data"); + return 0; + } + // calculate authCookie + if (secret) { + i2p::crypto::X25519Keys ck(secret, nullptr); // derive cpk_i from csk_i + uint8_t authInput[100]; + ck.Agree(ephemeralPublicKey, authInput); // sharedSecret is first 32 bytes of authInput + memcpy(authInput + 32, ck.GetPublicKey(), 32); // cpk_i + memcpy(authInput + 64, subcredential, 36); + uint8_t okm[64]; // 52 actual data + i2p::crypto::HKDF(ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm); + if (!GetAuthCookie(authClients, numClients, okm, authCookie)) + LogPrint(eLogError, "LeaseSet2: Client cookie DH not found"); + } else + LogPrint(eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided"); + } else if (flag & 0x02) // PSK, bit 1 is set to 1 + { + const uint8_t *authSalt = buf + offset; + offset += 32; // authSalt + uint16_t numClients = bufbe16toh(buf + offset); + offset += 2; // clients + const uint8_t *authClients = buf + offset; + offset += numClients * 40; // authClients + if (offset > len) { + LogPrint(eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data"); + return 0; + } + // calculate authCookie + if (secret) { + uint8_t authInput[68]; + memcpy(authInput, secret, 32); + memcpy(authInput + 32, subcredential, 36); + uint8_t okm[64]; // 52 actual data + i2p::crypto::HKDF(authSalt, authInput, 68, "ELS2PSKA", okm); + if (!GetAuthCookie(authClients, numClients, okm, authCookie)) + LogPrint(eLogError, "LeaseSet2: Client cookie PSK not found"); + } else + LogPrint(eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided"); + } else + LogPrint(eLogError, "LeaseSet2: Unknown client auth type ", (int) flag); + } + return offset - 1; + } - void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted) const - { - auto encryptor = m_Encryptor; // TODO: atomic - if (encryptor) - encryptor->Encrypt (data, encrypted); - } + void LeaseSet2::Encrypt(const uint8_t *data, uint8_t *encrypted) const { + auto encryptor = m_Encryptor; // TODO: atomic + if (encryptor) + encryptor->Encrypt(data, encrypted); + } - uint64_t LeaseSet2::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const - { - uint64_t expiration = 0; - ExtractPublishedTimestamp (buf, len, expiration); - return expiration; - } + uint64_t LeaseSet2::ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const { + uint64_t expiration = 0; + ExtractPublishedTimestamp(buf, len, expiration); + return expiration; + } - uint64_t LeaseSet2::ExtractPublishedTimestamp (const uint8_t * buf, size_t len, uint64_t& expiration) const - { - if (len < 8) return 0; - if (m_StoreType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) - { - // encrypted LS2 - size_t offset = 0; - uint16_t blindedKeyType = bufbe16toh (buf + offset); offset += 2; - std::unique_ptr blindedVerifier (i2p::data::IdentityEx::CreateVerifier (blindedKeyType)); - if (!blindedVerifier) return 0 ; - auto blindedKeyLen = blindedVerifier->GetPublicKeyLen (); - if (offset + blindedKeyLen + 6 >= len) return 0; - offset += blindedKeyLen; - uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; - uint16_t expires = bufbe16toh (buf + offset); offset += 2; - expiration = (timestamp + expires)* 1000LL; - return timestamp; - } - else - { - auto identity = GetIdentity (); - if (!identity) return 0; - size_t offset = identity->GetFullLen (); - if (offset + 6 >= len) return 0; - uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; - uint16_t expires = bufbe16toh (buf + offset); offset += 2; - expiration = (timestamp + expires)* 1000LL; - return timestamp; - } - } + uint64_t LeaseSet2::ExtractPublishedTimestamp(const uint8_t *buf, size_t len, uint64_t &expiration) const { + if (len < 8) return 0; + if (m_StoreType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { + // encrypted LS2 + size_t offset = 0; + uint16_t blindedKeyType = bufbe16toh(buf + offset); + offset += 2; + std::unique_ptr blindedVerifier( + i2p::data::IdentityEx::CreateVerifier(blindedKeyType)); + if (!blindedVerifier) return 0; + auto blindedKeyLen = blindedVerifier->GetPublicKeyLen(); + if (offset + blindedKeyLen + 6 >= len) return 0; + offset += blindedKeyLen; + uint32_t timestamp = bufbe32toh(buf + offset); + offset += 4; + uint16_t expires = bufbe16toh(buf + offset); + offset += 2; + expiration = (timestamp + expires) * 1000LL; + return timestamp; + } else { + auto identity = GetIdentity(); + if (!identity) return 0; + size_t offset = identity->GetFullLen(); + if (offset + 6 >= len) return 0; + uint32_t timestamp = bufbe32toh(buf + offset); + offset += 4; + uint16_t expires = bufbe16toh(buf + offset); + offset += 2; + expiration = (timestamp + expires) * 1000LL; + return timestamp; + } + } - LocalLeaseSet::LocalLeaseSet (std::shared_ptr identity, const uint8_t * encryptionPublicKey, std::vector > tunnels): - m_ExpirationTime (0), m_Identity (identity) - { - int num = tunnels.size (); - if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; - // identity - auto signingKeyLen = m_Identity->GetSigningPublicKeyLen (); - m_BufferLen = m_Identity->GetFullLen () + 256 + signingKeyLen + 1 + num*LEASE_SIZE + m_Identity->GetSignatureLen (); - m_Buffer = new uint8_t[m_BufferLen]; - auto offset = m_Identity->ToBuffer (m_Buffer, m_BufferLen); - memcpy (m_Buffer + offset, encryptionPublicKey, 256); - offset += 256; - memset (m_Buffer + offset, 0, signingKeyLen); - offset += signingKeyLen; - // num leases - m_Buffer[offset] = num; - offset++; - // leases - m_Leases = m_Buffer + offset; - auto currentTime = i2p::util::GetMillisecondsSinceEpoch (); - for (int i = 0; i < num; i++) - { - memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32); - offset += 32; // gateway id - htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ()); - offset += 4; // tunnel id - uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration - ts *= 1000; // in milliseconds - if (ts > m_ExpirationTime) m_ExpirationTime = ts; - // make sure leaseset is newer than previous, but adding some time to expiration date - ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs - htobe64buf (m_Buffer + offset, ts); - offset += 8; // end date - } - // we don't sign it yet. must be signed later on - } + LocalLeaseSet::LocalLeaseSet(std::shared_ptr identity, const uint8_t *encryptionPublicKey, + std::vector > tunnels) : + m_ExpirationTime(0), m_Identity(identity) { + int num = tunnels.size(); + if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; + // identity + auto signingKeyLen = m_Identity->GetSigningPublicKeyLen(); + m_BufferLen = m_Identity->GetFullLen() + 256 + signingKeyLen + 1 + num * LEASE_SIZE + + m_Identity->GetSignatureLen(); + m_Buffer = new uint8_t[m_BufferLen]; + auto offset = m_Identity->ToBuffer(m_Buffer, m_BufferLen); + memcpy(m_Buffer + offset, encryptionPublicKey, 256); + offset += 256; + memset(m_Buffer + offset, 0, signingKeyLen); + offset += signingKeyLen; + // num leases + m_Buffer[offset] = num; + offset++; + // leases + m_Leases = m_Buffer + offset; + auto currentTime = i2p::util::GetMillisecondsSinceEpoch(); + for (int i = 0; i < num; i++) { + memcpy(m_Buffer + offset, tunnels[i]->GetNextIdentHash(), 32); + offset += 32; // gateway id + htobe32buf(m_Buffer + offset, tunnels[i]->GetNextTunnelID()); + offset += 4; // tunnel id + uint64_t ts = tunnels[i]->GetCreationTime() + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - + i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration + ts *= 1000; // in milliseconds + if (ts > m_ExpirationTime) m_ExpirationTime = ts; + // make sure leaseset is newer than previous, but adding some time to expiration date + ts += (currentTime - tunnels[i]->GetCreationTime() * 1000LL) * 2 / + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs + htobe64buf(m_Buffer + offset, ts); + offset += 8; // end date + } + // we don't sign it yet. must be signed later on + } - LocalLeaseSet::LocalLeaseSet (std::shared_ptr identity, const uint8_t * buf, size_t len): - m_ExpirationTime (0), m_Identity (identity) - { - if (buf) - { - m_BufferLen = len; - m_Buffer = new uint8_t[m_BufferLen]; - memcpy (m_Buffer, buf, len); - } - else - { - m_Buffer = nullptr; - m_BufferLen = 0; - } - } + LocalLeaseSet::LocalLeaseSet(std::shared_ptr identity, const uint8_t *buf, size_t len) : + m_ExpirationTime(0), m_Identity(identity) { + if (buf) { + m_BufferLen = len; + m_Buffer = new uint8_t[m_BufferLen]; + memcpy(m_Buffer, buf, len); + } else { + m_Buffer = nullptr; + m_BufferLen = 0; + } + } - bool LocalLeaseSet::IsExpired () const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - return ts > m_ExpirationTime; - } + bool LocalLeaseSet::IsExpired() const { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + return ts > m_ExpirationTime; + } - bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires) - { - IdentityEx ident(ptr, sz); - size_t size = ident.GetFullLen (); - if (size > sz) - { - LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", sz); - return false; - } - // encryption key - size += 256; - // signing key (unused) - size += ident.GetSigningPublicKeyLen (); - uint8_t numLeases = ptr[size]; - ++size; - if (!numLeases || numLeases > MAX_NUM_LEASES) - { - LogPrint (eLogError, "LeaseSet: Incorrect number of leases", (int)numLeases); - return false; - } - const uint8_t * leases = ptr + size; - expires = 0; - /** find lease with the max expiration timestamp */ - for (int i = 0; i < numLeases; i++) - { - leases += 36; // gateway + tunnel ID - uint64_t endDate = bufbe64toh (leases); - leases += 8; // end date - if(endDate > expires) - expires = endDate; - } - return ident.Verify(ptr, leases - ptr, leases); - } + bool LeaseSetBufferValidate(const uint8_t *ptr, size_t sz, uint64_t &expires) { + IdentityEx ident(ptr, sz); + size_t size = ident.GetFullLen(); + if (size > sz) { + LogPrint(eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", sz); + return false; + } + // encryption key + size += 256; + // signing key (unused) + size += ident.GetSigningPublicKeyLen(); + uint8_t numLeases = ptr[size]; + ++size; + if (!numLeases || numLeases > MAX_NUM_LEASES) { + LogPrint(eLogError, "LeaseSet: Incorrect number of leases", (int) numLeases); + return false; + } + const uint8_t *leases = ptr + size; + expires = 0; + /** find lease with the max expiration timestamp */ + for (int i = 0; i < numLeases; i++) { + leases += 36; // gateway + tunnel ID + uint64_t endDate = bufbe64toh(leases); + leases += 8; // end date + if (endDate > expires) + expires = endDate; + } + return ident.Verify(ptr, leases - ptr, leases); + } - LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - const KeySections& encryptionKeys, const std::vector >& tunnels, - bool isPublic, bool isPublishedEncrypted): - LocalLeaseSet (keys.GetPublic (), nullptr, 0) - { - auto identity = keys.GetPublic (); - // assume standard LS2 - int num = tunnels.size (); - if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; - size_t keySectionsLen = 0; - for (const auto& it: encryptionKeys) - keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/; - m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ + - 1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen (); - uint16_t flags = 0; - if (keys.IsOfflineSignature ()) - { - flags |= LEASESET2_FLAG_OFFLINE_KEYS; - m_BufferLen += keys.GetOfflineSignature ().size (); - } - if (isPublishedEncrypted) - { - flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED; - isPublic = true; - } - if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET; + LocalLeaseSet2::LocalLeaseSet2(uint8_t storeType, const i2p::data::PrivateKeys &keys, + const KeySections &encryptionKeys, + const std::vector > &tunnels, + bool isPublic, bool isPublishedEncrypted) : + LocalLeaseSet(keys.GetPublic(), nullptr, 0) { + auto identity = keys.GetPublic(); + // assume standard LS2 + int num = tunnels.size(); + if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; + size_t keySectionsLen = 0; + for (const auto &it: encryptionKeys) + keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/; + m_BufferLen = identity->GetFullLen() + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ + + 1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num * LEASE2_SIZE + keys.GetSignatureLen(); + uint16_t flags = 0; + if (keys.IsOfflineSignature()) { + flags |= LEASESET2_FLAG_OFFLINE_KEYS; + m_BufferLen += keys.GetOfflineSignature().size(); + } + if (isPublishedEncrypted) { + flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED; + isPublic = true; + } + if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET; - m_Buffer = new uint8_t[m_BufferLen + 1]; - m_Buffer[0] = storeType; - // LS2 header - auto offset = identity->ToBuffer (m_Buffer + 1, m_BufferLen) + 1; - auto timestamp = i2p::util::GetSecondsSinceEpoch (); - htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds) - uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later - htobe16buf (m_Buffer + offset, flags); offset += 2; // flags - if (keys.IsOfflineSignature ()) - { - // offline signature - const auto& offlineSignature = keys.GetOfflineSignature (); - memcpy (m_Buffer + offset, offlineSignature.data (), offlineSignature.size ()); - offset += offlineSignature.size (); - } - htobe16buf (m_Buffer + offset, 0); offset += 2; // properties len - // keys - m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key - for (const auto& it: encryptionKeys) - { - htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type - htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len - memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key - } - // leases - uint32_t expirationTime = 0; // in seconds - m_Buffer[offset] = num; offset++; // num leases - for (int i = 0; i < num; i++) - { - memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32); - offset += 32; // gateway id - htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ()); - offset += 4; // tunnel id - auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration - if (ts > expirationTime) expirationTime = ts; - htobe32buf (m_Buffer + offset, ts); - offset += 4; // end date - } - // update expiration - if (expirationTime) - { - SetExpirationTime (expirationTime*1000LL); - auto expires = (int)expirationTime - timestamp; - htobe16buf (expiresBuf, expires > 0 ? expires : 0); - } - else - { - // no tunnels or withdraw - SetExpirationTime (timestamp*1000LL); - memset (expiresBuf, 0, 2); // expires immeditely - } - // sign - keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type - } + m_Buffer = new uint8_t[m_BufferLen + 1]; + m_Buffer[0] = storeType; + // LS2 header + auto offset = identity->ToBuffer(m_Buffer + 1, m_BufferLen) + 1; + auto timestamp = i2p::util::GetSecondsSinceEpoch(); + htobe32buf(m_Buffer + offset, timestamp); + offset += 4; // published timestamp (seconds) + uint8_t *expiresBuf = m_Buffer + offset; + offset += 2; // expires, fill later + htobe16buf(m_Buffer + offset, flags); + offset += 2; // flags + if (keys.IsOfflineSignature()) { + // offline signature + const auto &offlineSignature = keys.GetOfflineSignature(); + memcpy(m_Buffer + offset, offlineSignature.data(), offlineSignature.size()); + offset += offlineSignature.size(); + } + htobe16buf(m_Buffer + offset, 0); + offset += 2; // properties len + // keys + m_Buffer[offset] = encryptionKeys.size(); + offset++; // 1 key + for (const auto &it: encryptionKeys) { + htobe16buf(m_Buffer + offset, it.keyType); + offset += 2; // key type + htobe16buf(m_Buffer + offset, it.keyLen); + offset += 2; // key len + memcpy(m_Buffer + offset, it.encryptionPublicKey, it.keyLen); + offset += it.keyLen; // key + } + // leases + uint32_t expirationTime = 0; // in seconds + m_Buffer[offset] = num; + offset++; // num leases + for (int i = 0; i < num; i++) { + memcpy(m_Buffer + offset, tunnels[i]->GetNextIdentHash(), 32); + offset += 32; // gateway id + htobe32buf(m_Buffer + offset, tunnels[i]->GetNextTunnelID()); + offset += 4; // tunnel id + auto ts = tunnels[i]->GetCreationTime() + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - + i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration + if (ts > expirationTime) expirationTime = ts; + htobe32buf(m_Buffer + offset, ts); + offset += 4; // end date + } + // update expiration + if (expirationTime) { + SetExpirationTime(expirationTime * 1000LL); + auto expires = (int) expirationTime - timestamp; + htobe16buf(expiresBuf, expires > 0 ? expires : 0); + } else { + // no tunnels or withdraw + SetExpirationTime(timestamp * 1000LL); + memset(expiresBuf, 0, 2); // expires immeditely + } + // sign + keys.Sign(m_Buffer, offset, m_Buffer + offset); // LS + leading store type + } - LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len): - LocalLeaseSet (identity, nullptr, 0) - { - m_BufferLen = len; - m_Buffer = new uint8_t[m_BufferLen + 1]; - memcpy (m_Buffer + 1, buf, len); - m_Buffer[0] = storeType; - } + LocalLeaseSet2::LocalLeaseSet2(uint8_t storeType, std::shared_ptr identity, + const uint8_t *buf, size_t len) : + LocalLeaseSet(identity, nullptr, 0) { + m_BufferLen = len; + m_Buffer = new uint8_t[m_BufferLen + 1]; + memcpy(m_Buffer + 1, buf, len); + m_Buffer[0] = storeType; + } - LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, - int authType, std::shared_ptr > authKeys): - LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls) - { - size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1; - uint8_t layer1Flags = 0; - if (authKeys) - { - if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1 - else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1 - if (layer1Flags) - lenOuterPlaintext += 32 + 2 + authKeys->size ()*40; // auth data len - } - size_t lenOuterCiphertext = lenOuterPlaintext + 32; + LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2(std::shared_ptr ls, + const i2p::data::PrivateKeys &keys, + int authType, + std::shared_ptr > authKeys) : + LocalLeaseSet2(ls->GetIdentity()), m_InnerLeaseSet(ls) { + size_t lenInnerPlaintext = ls->GetBufferLen() + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1; + uint8_t layer1Flags = 0; + if (authKeys) { + if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) + layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1 + else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) + layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1 + if (layer1Flags) + lenOuterPlaintext += 32 + 2 + authKeys->size() * 40; // auth data len + } + size_t lenOuterCiphertext = lenOuterPlaintext + 32; - m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/; - m_Buffer = new uint8_t[m_BufferLen + 1]; - m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; - BlindedPublicKey blindedKey (ls->GetIdentity ()); - auto timestamp = i2p::util::GetSecondsSinceEpoch (); - char date[9]; - i2p::util::GetDateString (timestamp, date); - uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max - size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub); - std::unique_ptr blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv)); - if (!blindedSigner) - { - LogPrint (eLogError, "LeaseSet2: Can't create blinded signer for signature type ", blindedKey.GetSigType ()); - return; - } - auto offset = 1; - htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type - memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key - htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds) - auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds - auto expirationTime = ls->GetExpirationTime ()/1000LL; - if (expirationTime > nextMidnight) expirationTime = nextMidnight; - SetExpirationTime (expirationTime*1000LL); - htobe16buf (m_Buffer + offset, expirationTime > timestamp ? expirationTime - timestamp : 0); offset += 2; // expires - uint16_t flags = 0; - htobe16buf (m_Buffer + offset, flags); offset += 2; // flags - htobe16buf (m_Buffer + offset, lenOuterCiphertext); offset += 2; // lenOuterCiphertext - // outerChipherText - // Layer 1 - uint8_t subcredential[36]; - blindedKey.GetSubcredential (blindedPub, 32, subcredential); - htobe32buf (subcredential + 32, timestamp); // outerInput = subcredential || publishedTimestamp - // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) - uint8_t keys1[64]; // 44 bytes actual data - RAND_bytes (m_Buffer + offset, 32); // outerSalt = CSRNG(32) - i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1); - offset += 32; // outerSalt - uint8_t * outerPlainText = m_Buffer + offset; - m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags - // auth data - uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp - if (layer1Flags) - { - RAND_bytes (innerInput, 32); // authCookie - CreateClientAuthData (subcredential, authType, authKeys, innerInput, m_Buffer + offset); - offset += 32 + 2 + authKeys->size ()*40; // auth clients - } - // Layer 2 - // keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44) - uint8_t keys2[64]; // 44 bytes actual data - RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32) - if (layer1Flags) - { - memcpy (innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp - i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2); - } - else - i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie - offset += 32; // innerSalt - m_Buffer[offset] = ls->GetStoreType (); - memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ()); - i2p::crypto::ChaCha20 (m_Buffer + offset, lenInnerPlaintext, keys2, keys2 + 32, m_Buffer + offset); // encrypt Layer 2 - offset += lenInnerPlaintext; - i2p::crypto::ChaCha20 (outerPlainText, lenOuterPlaintext, keys1, keys1 + 32, outerPlainText); // encrypt Layer 1 - // signature - blindedSigner->Sign (m_Buffer, offset, m_Buffer + offset); - // store hash - m_StoreHash = blindedKey.GetStoreHash (date); - } + m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/; + m_Buffer = new uint8_t[m_BufferLen + 1]; + m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; + BlindedPublicKey blindedKey(ls->GetIdentity()); + auto timestamp = i2p::util::GetSecondsSinceEpoch(); + char date[9]; + i2p::util::GetDateString(timestamp, date); + uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max + size_t publicKeyLen = blindedKey.BlindPrivateKey(keys.GetSigningPrivateKey(), date, blindedPriv, + blindedPub); + std::unique_ptr blindedSigner( + i2p::data::PrivateKeys::CreateSigner(blindedKey.GetBlindedSigType(), blindedPriv)); + if (!blindedSigner) { + LogPrint(eLogError, "LeaseSet2: Can't create blinded signer for signature type ", + blindedKey.GetSigType()); + return; + } + auto offset = 1; + htobe16buf(m_Buffer + offset, blindedKey.GetBlindedSigType()); + offset += 2; // Blinded Public Key Sig Type + memcpy(m_Buffer + offset, blindedPub, publicKeyLen); + offset += publicKeyLen; // Blinded Public Key + htobe32buf(m_Buffer + offset, timestamp); + offset += 4; // published timestamp (seconds) + auto nextMidnight = (timestamp / 86400LL + 1) * 86400LL; // 86400 = 24*3600 seconds + auto expirationTime = ls->GetExpirationTime() / 1000LL; + if (expirationTime > nextMidnight) expirationTime = nextMidnight; + SetExpirationTime(expirationTime * 1000LL); + htobe16buf(m_Buffer + offset, expirationTime > timestamp ? expirationTime - timestamp : 0); + offset += 2; // expires + uint16_t flags = 0; + htobe16buf(m_Buffer + offset, flags); + offset += 2; // flags + htobe16buf(m_Buffer + offset, lenOuterCiphertext); + offset += 2; // lenOuterCiphertext + // outerChipherText + // Layer 1 + uint8_t subcredential[36]; + blindedKey.GetSubcredential(blindedPub, 32, subcredential); + htobe32buf(subcredential + 32, timestamp); // outerInput = subcredential || publishedTimestamp + // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) + uint8_t keys1[64]; // 44 bytes actual data + RAND_bytes(m_Buffer + offset, 32); // outerSalt = CSRNG(32) + i2p::crypto::HKDF(m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1); + offset += 32; // outerSalt + uint8_t *outerPlainText = m_Buffer + offset; + m_Buffer[offset] = layer1Flags; + offset++; // layer 1 flags + // auth data + uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp + if (layer1Flags) { + RAND_bytes(innerInput, 32); // authCookie + CreateClientAuthData(subcredential, authType, authKeys, innerInput, m_Buffer + offset); + offset += 32 + 2 + authKeys->size() * 40; // auth clients + } + // Layer 2 + // keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44) + uint8_t keys2[64]; // 44 bytes actual data + RAND_bytes(m_Buffer + offset, 32); // innerSalt = CSRNG(32) + if (layer1Flags) { + memcpy(innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp + i2p::crypto::HKDF(m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2); + } else + i2p::crypto::HKDF(m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie + offset += 32; // innerSalt + m_Buffer[offset] = ls->GetStoreType(); + memcpy(m_Buffer + offset + 1, ls->GetBuffer(), ls->GetBufferLen()); + i2p::crypto::ChaCha20(m_Buffer + offset, lenInnerPlaintext, keys2, keys2 + 32, + m_Buffer + offset); // encrypt Layer 2 + offset += lenInnerPlaintext; + i2p::crypto::ChaCha20(outerPlainText, lenOuterPlaintext, keys1, keys1 + 32, + outerPlainText); // encrypt Layer 1 + // signature + blindedSigner->Sign(m_Buffer, offset, m_Buffer + offset); + // store hash + m_StoreHash = blindedKey.GetStoreHash(date); + } - LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr identity, const uint8_t * buf, size_t len): - LocalLeaseSet2 (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, identity, buf, len) - { - // fill inner LeaseSet2 - auto blindedKey = std::make_shared(identity); - i2p::data::LeaseSet2 ls (buf, len, blindedKey); // inner layer - if (ls.IsValid ()) - { - m_InnerLeaseSet = std::make_shared(ls.GetStoreType (), identity, ls.GetBuffer (), ls.GetBufferLen ()); - m_StoreHash = blindedKey->GetStoreHash (); - } - else - LogPrint (eLogError, "LeaseSet2: Couldn't extract inner layer"); - } + LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2(std::shared_ptr identity, const uint8_t *buf, + size_t len) : + LocalLeaseSet2(NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, identity, buf, len) { + // fill inner LeaseSet2 + auto blindedKey = std::make_shared(identity); + i2p::data::LeaseSet2 ls(buf, len, blindedKey); // inner layer + if (ls.IsValid()) { + m_InnerLeaseSet = std::make_shared(ls.GetStoreType(), identity, ls.GetBuffer(), + ls.GetBufferLen()); + m_StoreHash = blindedKey->GetStoreHash(); + } else + LogPrint(eLogError, "LeaseSet2: Couldn't extract inner layer"); + } - void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr > authKeys, const uint8_t * authCookie, uint8_t * authData) const - { - if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) - { - i2p::crypto::X25519Keys ek; - ek.GenerateKeys (); // esk and epk - memcpy (authData, ek.GetPublicKey (), 32); authData += 32; // epk - htobe16buf (authData, authKeys->size ()); authData += 2; // num clients - uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp - memcpy (authInput + 64, subcredential, 36); - for (auto& it: *authKeys) - { - ek.Agree (it, authInput); // sharedSecret = DH(esk, cpk_i) - memcpy (authInput + 32, it, 32); - uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm); - memcpy (authData, okm + 44, 8); authData += 8; // clientID_i - i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i - } - } - else // assume PSK - { - uint8_t authSalt[32]; - RAND_bytes (authSalt, 32); - memcpy (authData, authSalt, 32); authData += 32; // authSalt - htobe16buf (authData, authKeys->size ()); authData += 2; // num clients - uint8_t authInput[68]; // authInput = psk_i || subcredential || publishedTimestamp - memcpy (authInput + 32, subcredential, 36); - for (auto& it: *authKeys) - { - memcpy (authInput, it, 32); - uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm); - memcpy (authData, okm + 44, 8); authData += 8; // clientID_i - i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i - } - } - } -} + void LocalEncryptedLeaseSet2::CreateClientAuthData(const uint8_t *subcredential, int authType, + std::shared_ptr > authKeys, + const uint8_t *authCookie, uint8_t *authData) const { + if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) { + i2p::crypto::X25519Keys ek; + ek.GenerateKeys(); // esk and epk + memcpy(authData, ek.GetPublicKey(), 32); + authData += 32; // epk + htobe16buf(authData, authKeys->size()); + authData += 2; // num clients + uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp + memcpy(authInput + 64, subcredential, 36); + for (auto &it: *authKeys) { + ek.Agree(it, authInput); // sharedSecret = DH(esk, cpk_i) + memcpy(authInput + 32, it, 32); + uint8_t okm[64]; // 52 actual data + i2p::crypto::HKDF(ek.GetPublicKey(), authInput, 100, "ELS2_XCA", okm); + memcpy(authData, okm + 44, 8); + authData += 8; // clientID_i + i2p::crypto::ChaCha20(authCookie, 32, okm, okm + 32, authData); + authData += 32; // clientCookie_i + } + } else // assume PSK + { + uint8_t authSalt[32]; + RAND_bytes(authSalt, 32); + memcpy(authData, authSalt, 32); + authData += 32; // authSalt + htobe16buf(authData, authKeys->size()); + authData += 2; // num clients + uint8_t authInput[68]; // authInput = psk_i || subcredential || publishedTimestamp + memcpy(authInput + 32, subcredential, 36); + for (auto &it: *authKeys) { + memcpy(authInput, it, 32); + uint8_t okm[64]; // 52 actual data + i2p::crypto::HKDF(authSalt, authInput, 68, "ELS2PSKA", okm); + memcpy(authData, okm + 44, 8); + authData += 8; // clientID_i + i2p::crypto::ChaCha20(authCookie, 32, okm, okm + 32, authData); + authData += 32; // clientCookie_i + } + } + } + } } diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index a79a5870..7abffe26 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -19,288 +19,360 @@ #include "I2PEndian.h" #include "Blinding.h" -namespace i2p -{ +namespace i2p { -namespace tunnel -{ - class InboundTunnel; -} + namespace tunnel { + class InboundTunnel; + } -namespace data -{ - const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds - struct Lease - { - IdentHash tunnelGateway; - uint32_t tunnelID; - uint64_t endDate; // 0 means invalid - bool isUpdated; // transient - /* return true if this lease expires within t millisecond + fudge factor */ - bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const { - auto expire = i2p::util::GetMillisecondsSinceEpoch (); - if(fudge) expire += rand() % fudge; - if (endDate < expire) return true; - return (endDate - expire) < t; - } - }; + namespace data { + const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds + struct Lease { + IdentHash tunnelGateway; + uint32_t tunnelID; + uint64_t endDate; // 0 means invalid + bool isUpdated; // transient + /* return true if this lease expires within t millisecond + fudge factor */ + bool ExpiresWithin(const uint64_t t, const uint64_t fudge = 1000) const { + auto expire = i2p::util::GetMillisecondsSinceEpoch(); + if (fudge) expire += rand() % fudge; + if (endDate < expire) return true; + return (endDate - expire) < t; + } + }; - struct LeaseCmp - { - bool operator() (std::shared_ptr l1, std::shared_ptr l2) const - { - if (l1->tunnelID != l2->tunnelID) - return l1->tunnelID < l2->tunnelID; - else - return l1->tunnelGateway < l2->tunnelGateway; - }; - }; + struct LeaseCmp { + bool operator()(std::shared_ptr l1, std::shared_ptr l2) const { + if (l1->tunnelID != l2->tunnelID) + return l1->tunnelID < l2->tunnelID; + else + return l1->tunnelGateway < l2->tunnelGateway; + }; + }; - typedef std::function LeaseInspectFunc; + typedef std::function LeaseInspectFunc; - const size_t MAX_LS_BUFFER_SIZE = 3072; - const size_t LEASE_SIZE = 44; // 32 + 4 + 8 - const size_t LEASE2_SIZE = 40; // 32 + 4 + 4 - const uint8_t MAX_NUM_LEASES = 16; + const size_t MAX_LS_BUFFER_SIZE = 3072; + const size_t LEASE_SIZE = 44; // 32 + 4 + 8 + const size_t LEASE2_SIZE = 40; // 32 + 4 + 4 + const uint8_t MAX_NUM_LEASES = 16; - const uint8_t NETDB_STORE_TYPE_LEASESET = 1; - class LeaseSet: public RoutingDestination - { - public: + const uint8_t NETDB_STORE_TYPE_LEASESET = 1; - LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true); - virtual ~LeaseSet () { delete[] m_EncryptionKey; delete[] m_Buffer; }; - virtual void Update (const uint8_t * buf, size_t len, bool verifySignature = true); - virtual bool IsNewer (const uint8_t * buf, size_t len) const; - void PopulateLeases (); // from buffer + class LeaseSet : public RoutingDestination { + public: - const uint8_t * GetBuffer () const { return m_Buffer; }; - size_t GetBufferLen () const { return m_BufferLen; }; - bool IsValid () const { return m_IsValid; }; - const std::vector > GetNonExpiredLeases (bool withThreshold = true) const; - const std::vector > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const; - 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 GetTransientVerifier () const { return nullptr; }; - virtual bool IsPublishedEncrypted () const { return false; }; + LeaseSet(const uint8_t *buf, size_t len, bool storeLeases = true); - // implements RoutingDestination - std::shared_ptr GetIdentity () const { return m_Identity; }; - void Encrypt (const uint8_t * data, uint8_t * encrypted) const; - bool IsDestination () const { return true; }; + virtual ~LeaseSet() { + delete[] m_EncryptionKey; + delete[] m_Buffer; + }; - protected: + virtual void Update(const uint8_t *buf, size_t len, bool verifySignature = true); - void UpdateLeasesBegin (); - void UpdateLeasesEnd (); - void UpdateLease (const Lease& lease, uint64_t ts); + virtual bool IsNewer(const uint8_t *buf, size_t len) const; - // called from LeaseSet2 - LeaseSet (bool storeLeases); - void SetBuffer (const uint8_t * buf, size_t len); - void SetBufferLen (size_t len); - void SetIdentity (std::shared_ptr identity) { m_Identity = identity; }; - void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; }; - void SetIsValid (bool isValid) { m_IsValid = isValid; }; - bool IsStoreLeases () const { return m_StoreLeases; }; + void PopulateLeases(); // from buffer - private: + const uint8_t *GetBuffer() const { return m_Buffer; }; - void ReadFromBuffer (bool readIdentity = true, bool verifySignature = true); - virtual uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; // returns max expiration time + size_t GetBufferLen() const { return m_BufferLen; }; - private: + bool IsValid() const { return m_IsValid; }; - bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill - std::set, LeaseCmp> m_Leases; - uint64_t m_ExpirationTime; // in milliseconds - std::shared_ptr m_Identity; - uint8_t * m_EncryptionKey; - uint8_t * m_Buffer; - size_t m_BufferLen; - }; + const std::vector > GetNonExpiredLeases(bool withThreshold = true) const; - /** - * 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 std::vector > + GetNonExpiredLeasesExcluding(LeaseInspectFunc exclude, bool withThreshold = true) const; - 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; + bool HasExpiredLeases() const; - const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; - const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002; - const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004; + bool IsExpired() const; - class LeaseSet2: public LeaseSet - { - public: + bool IsEmpty() const { return m_Leases.empty(); }; - 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 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 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; + uint64_t GetExpirationTime() const { return m_ExpirationTime; }; - // implements RoutingDestination - void Encrypt (const uint8_t * data, uint8_t * encrypted) const; - CryptoKeyType GetEncryptionType () const { return m_EncryptionType; }; + bool ExpiresSoon(const uint64_t dlt = 1000 * 5, const uint64_t fudge = 0) const; - 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); - void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr 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); + virtual uint8_t GetStoreType() const { return NETDB_STORE_TYPE_LEASESET; }; - template - bool VerifySignature (Verifier& verifier, const uint8_t * buf, size_t len, size_t signatureOffset); + virtual uint32_t GetPublishedTimestamp() const { return 0; }; // should be set for LeaseSet2 only + virtual std::shared_ptr GetTransientVerifier() const { return nullptr; }; - 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 + virtual bool IsPublishedEncrypted() const { return false; }; - private: + // implements RoutingDestination + std::shared_ptr GetIdentity() const { return m_Identity; }; - uint8_t m_StoreType; - uint32_t m_PublishedTimestamp = 0; - bool m_IsPublic = true, m_IsPublishedEncrypted = false; - std::shared_ptr m_TransientVerifier; - CryptoKeyType m_EncryptionType; - std::shared_ptr m_Encryptor; // for standardLS2 - }; + void Encrypt(const uint8_t *data, uint8_t *encrypted) const; - // also called from Streaming.cpp - template - std::shared_ptr 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 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; - } + bool IsDestination() const { return true; }; + + protected: + + void UpdateLeasesBegin(); + + void UpdateLeasesEnd(); + + void UpdateLease(const Lease &lease, uint64_t ts); + + // called from LeaseSet2 + LeaseSet(bool storeLeases); + + void SetBuffer(const uint8_t *buf, size_t len); + + void SetBufferLen(size_t len); + + void SetIdentity(std::shared_ptr identity) { m_Identity = identity; }; + + void SetExpirationTime(uint64_t t) { m_ExpirationTime = t; }; + + void SetIsValid(bool isValid) { m_IsValid = isValid; }; + + 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, LeaseCmp> m_Leases; + uint64_t m_ExpirationTime; // in milliseconds + std::shared_ptr 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 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 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 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 + 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 m_TransientVerifier; + CryptoKeyType m_EncryptionType; + std::shared_ptr m_Encryptor; // for standardLS2 + }; + + // also called from Streaming.cpp + template + std::shared_ptr + 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 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 - { - public: + class LocalLeaseSet { + public: - LocalLeaseSet (std::shared_ptr identity, const uint8_t * encryptionPublicKey, std::vector > tunnels); - LocalLeaseSet (std::shared_ptr identity, const uint8_t * buf, size_t len); - virtual ~LocalLeaseSet () { delete[] m_Buffer; }; + LocalLeaseSet(std::shared_ptr identity, const uint8_t *encryptionPublicKey, + std::vector > tunnels); - virtual uint8_t * GetBuffer () const { return m_Buffer; }; - 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; }; + LocalLeaseSet(std::shared_ptr identity, const uint8_t *buf, size_t len); - const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; - std::shared_ptr 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 ~LocalLeaseSet() { delete[] m_Buffer; }; - virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; - virtual const IdentHash& GetStoreHash () const { return GetIdentHash (); }; // differ from ident hash for encrypted LeaseSet2 - virtual std::shared_ptr GetInnerLeaseSet () const { return nullptr; }; // non-null for encrypted LeaseSet2 + virtual uint8_t *GetBuffer() const { return m_Buffer; }; - private: + uint8_t *GetSignature() { return GetBuffer() + GetBufferLen() - GetSignatureLen(); }; - uint64_t m_ExpirationTime; // in milliseconds - std::shared_ptr m_Identity; - uint8_t * m_Buffer, * m_Leases; - size_t m_BufferLen; - }; + virtual size_t GetBufferLen() const { return m_BufferLen; }; - class LocalLeaseSet2: public LocalLeaseSet - { - public: + size_t GetSignatureLen() const { return m_Identity->GetSignatureLen(); }; - struct KeySection - { - uint16_t keyType, keyLen; - const uint8_t * encryptionPublicKey; - }; - typedef std::vector KeySections; + uint8_t *GetLeases() { return m_Leases; }; - LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - const KeySections& encryptionKeys, - const std::vector >& tunnels, - bool isPublic, bool isPublishedEncrypted = false); + const IdentHash &GetIdentHash() const { return m_Identity->GetIdentHash(); }; - LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP + std::shared_ptr GetIdentity() const { return m_Identity; }; - virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; + bool IsExpired() const; - uint8_t * GetBuffer () const { return m_Buffer + 1; }; - size_t GetBufferLen () const { return m_BufferLen; }; + uint64_t GetExpirationTime() const { return m_ExpirationTime; }; - 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 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 + GetInnerLeaseSet() const { return nullptr; }; // non-null for encrypted LeaseSet2 - uint8_t * m_Buffer; // 1 byte store type + actual buffer - size_t m_BufferLen; - }; + private: + + uint64_t m_ExpirationTime; // in milliseconds + std::shared_ptr 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 KeySections; + + LocalLeaseSet2(uint8_t storeType, const i2p::data::PrivateKeys &keys, + const KeySections &encryptionKeys, + const std::vector > &tunnels, + bool isPublic, bool isPublishedEncrypted = false); + + LocalLeaseSet2(uint8_t storeType, std::shared_ptr identity, const uint8_t *buf, + size_t len); // from I2CP + + 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 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_DH = 1; - const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2; + const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0; + const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1; + const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2; - typedef i2p::data::Tag<32> AuthPublicKey; + typedef i2p::data::Tag<32> AuthPublicKey; - class LocalEncryptedLeaseSet2: public LocalLeaseSet2 - { - public: + class LocalEncryptedLeaseSet2 : public LocalLeaseSet2 { + public: - LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr > authKeys = nullptr); + LocalEncryptedLeaseSet2(std::shared_ptr ls, const i2p::data::PrivateKeys &keys, + int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, + std::shared_ptr > authKeys = nullptr); - LocalEncryptedLeaseSet2 (std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP + LocalEncryptedLeaseSet2(std::shared_ptr identity, const uint8_t *buf, + size_t len); // from I2CP - const IdentHash& GetStoreHash () const { return m_StoreHash; }; - std::shared_ptr GetInnerLeaseSet () const { return m_InnerLeaseSet; }; + const IdentHash &GetStoreHash() const { return m_StoreHash; }; - private: + std::shared_ptr GetInnerLeaseSet() const { return m_InnerLeaseSet; }; - void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr > authKeys, const uint8_t * authCookie, uint8_t * authData) const; + private: - private: + void CreateClientAuthData(const uint8_t *subcredential, int authType, + std::shared_ptr > authKeys, const uint8_t *authCookie, + uint8_t *authData) const; - IdentHash m_StoreHash; - std::shared_ptr m_InnerLeaseSet; - }; -} + private: + + IdentHash m_StoreHash; + std::shared_ptr m_InnerLeaseSet; + }; + } } #endif diff --git a/libi2pd/LittleBigEndian.h b/libi2pd/LittleBigEndian.h index 8c081187..0147b7b4 100644 --- a/libi2pd/LittleBigEndian.h +++ b/libi2pd/LittleBigEndian.h @@ -33,218 +33,186 @@ struct BigEndian; // Little-Endian template -#pragma pack(push,1) +#pragma pack(push, 1) + template -struct LittleEndian -{ - union - { - unsigned char bytes[sizeof(T)]; - T raw_value; - }; +struct LittleEndian { + union { + unsigned char bytes[sizeof(T)]; + T raw_value; + }; - LittleEndian(T t = T()) - { - operator =(t); - } + LittleEndian(T t = T()) { + operator=(t); + } - LittleEndian(const LittleEndian & t) - { - raw_value = t.raw_value; - } + LittleEndian(const LittleEndian &t) { + raw_value = t.raw_value; + } - LittleEndian(const BigEndian & t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[i] = t.bytes[sizeof(T)-1-i]; - } + LittleEndian(const BigEndian &t) { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[i] = t.bytes[sizeof(T) - 1 - i]; + } - operator const T() const - { - T t = T(); - for (unsigned i = 0; i < sizeof(T); i++) - t |= T(bytes[i]) << (i << 3); - return t; - } + operator const T() const { + T t = T(); + for (unsigned i = 0; i < sizeof(T); i++) + t |= T(bytes[i]) << (i << 3); + return t; + } - const T operator = (const T t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[sizeof(T)-1 - i] = static_cast(t >> (i << 3)); - return t; - } + const T operator=(const T t) { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[sizeof(T) - 1 - i] = static_cast(t >> (i << 3)); + return t; + } - // operators + // operators - const T operator += (const T t) - { - return (*this = *this + t); - } + const T operator+=(const T t) { + return (*this = *this + t); + } - const T operator -= (const T t) - { - return (*this = *this - t); - } + const T operator-=(const T t) { + return (*this = *this - t); + } - const T operator *= (const T t) - { - return (*this = *this * t); - } + const T operator*=(const T t) { + return (*this = *this * t); + } - const T operator /= (const T t) - { - return (*this = *this / t); - } + const T operator/=(const T t) { + return (*this = *this / t); + } - const T operator %= (const T t) - { - return (*this = *this % t); - } + const T operator%=(const T t) { + return (*this = *this % t); + } - LittleEndian operator ++ (int) - { - LittleEndian tmp(*this); - operator ++ (); - return tmp; - } + LittleEndian operator++(int) { + LittleEndian tmp(*this); + operator++(); + return tmp; + } - LittleEndian & operator ++ () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - ++bytes[i]; - if (bytes[i] != 0) - break; - } - return (*this); - } + LittleEndian &operator++() { + for (unsigned i = 0; i < sizeof(T); i++) { + ++bytes[i]; + if (bytes[i] != 0) + break; + } + return (*this); + } - LittleEndian operator -- (int) - { - LittleEndian tmp(*this); - operator -- (); - return tmp; - } + LittleEndian operator--(int) { + LittleEndian tmp(*this); + operator--(); + return tmp; + } - LittleEndian & operator -- () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - --bytes[i]; - if (bytes[i] != (T)(-1)) - break; - } - return (*this); - } + LittleEndian &operator--() { + for (unsigned i = 0; i < sizeof(T); i++) { + --bytes[i]; + if (bytes[i] != (T) (-1)) + break; + } + return (*this); + } }; + #pragma pack(pop) // Big-Endian template -#pragma pack(push,1) +#pragma pack(push, 1) + template -struct BigEndian -{ - union - { - unsigned char bytes[sizeof(T)]; - T raw_value; - }; +struct BigEndian { + union { + unsigned char bytes[sizeof(T)]; + T raw_value; + }; - BigEndian(T t = T()) - { - operator =(t); - } + BigEndian(T t = T()) { + operator=(t); + } - BigEndian(const BigEndian & t) - { - raw_value = t.raw_value; - } + BigEndian(const BigEndian &t) { + raw_value = t.raw_value; + } - BigEndian(const LittleEndian & t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[i] = t.bytes[sizeof(T)-1-i]; - } + BigEndian(const LittleEndian &t) { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[i] = t.bytes[sizeof(T) - 1 - i]; + } - operator const T() const - { - T t = T(); - for (unsigned i = 0; i < sizeof(T); i++) - t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); - return t; - } + operator const T() const { + T t = T(); + for (unsigned i = 0; i < sizeof(T); i++) + t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); + return t; + } - const T operator = (const T t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[sizeof(T) - 1 - i] = t >> (i << 3); - return t; - } + const T operator=(const T t) { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[sizeof(T) - 1 - i] = t >> (i << 3); + return t; + } - // operators + // operators - const T operator += (const T t) - { - return (*this = *this + t); - } + const T operator+=(const T t) { + return (*this = *this + t); + } - const T operator -= (const T t) - { - return (*this = *this - t); - } + const T operator-=(const T t) { + return (*this = *this - t); + } - const T operator *= (const T t) - { - return (*this = *this * t); - } + const T operator*=(const T t) { + return (*this = *this * t); + } - const T operator /= (const T t) - { - return (*this = *this / t); - } + const T operator/=(const T t) { + return (*this = *this / t); + } - const T operator %= (const T t) - { - return (*this = *this % t); - } + const T operator%=(const T t) { + return (*this = *this % t); + } - BigEndian operator ++ (int) - { - BigEndian tmp(*this); - operator ++ (); - return tmp; - } + BigEndian operator++(int) { + BigEndian tmp(*this); + operator++(); + return tmp; + } - BigEndian & operator ++ () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - ++bytes[sizeof(T) - 1 - i]; - if (bytes[sizeof(T) - 1 - i] != 0) - break; - } - return (*this); - } + BigEndian &operator++() { + for (unsigned i = 0; i < sizeof(T); i++) { + ++bytes[sizeof(T) - 1 - i]; + if (bytes[sizeof(T) - 1 - i] != 0) + break; + } + return (*this); + } - BigEndian operator -- (int) - { - BigEndian tmp(*this); - operator -- (); - return tmp; - } + BigEndian operator--(int) { + BigEndian tmp(*this); + operator--(); + return tmp; + } - BigEndian & operator -- () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - --bytes[sizeof(T) - 1 - i]; - if (bytes[sizeof(T) - 1 - i] != (T)(-1)) - break; - } - return (*this); - } + BigEndian &operator--() { + for (unsigned i = 0; i < sizeof(T); i++) { + --bytes[sizeof(T) - 1 - i]; + if (bytes[sizeof(T) - 1 - i] != (T) (-1)) + break; + } + return (*this); + } }; + #pragma pack(pop) #endif // LITTLEBIGENDIAN_H \ No newline at end of file diff --git a/libi2pd/Log.cpp b/libi2pd/Log.cpp index e90b5e2b..8beb8142 100644 --- a/libi2pd/Log.cpp +++ b/libi2pd/Log.cpp @@ -13,237 +13,243 @@ #include namespace i2p { -namespace log { - static Log logger; - /** - * @brief Maps our loglevel to their symbolic name - */ - static const char * g_LogLevelStr[eNumLogLevels] = - { - "none", // eLogNone - "error", // eLogError - "warn", // eLogWarn - "info", // eLogInfo - "debug" // eLogDebug - }; + namespace log { + static Log logger; + /** + * @brief Maps our loglevel to their symbolic name + */ + static const char *g_LogLevelStr[eNumLogLevels] = + { + "none", // eLogNone + "error", // eLogError + "warn", // eLogWarn + "info", // eLogInfo + "debug" // eLogDebug + }; - /** - * @brief Colorize log output -- array of terminal control sequences - * @note Using ISO 6429 (ANSI) color sequences - */ + /** + * @brief Colorize log output -- array of terminal control sequences + * @note Using ISO 6429 (ANSI) color sequences + */ #ifdef _WIN32 - static const char *LogMsgColors[] = { "", "", "", "", "", "" }; + static const char *LogMsgColors[] = { "", "", "", "", "", "" }; #else /* UNIX */ - static const char *LogMsgColors[] = { - [eLogNone] = "\033[0m", /* reset */ - [eLogError] = "\033[1;31m", /* red */ - [eLogWarning] = "\033[1;33m", /* yellow */ - [eLogInfo] = "\033[1;36m", /* cyan */ - [eLogDebug] = "\033[1;34m", /* blue */ - [eNumLogLevels] = "\033[0m", /* reset */ - }; + static const char *LogMsgColors[] = { + [eLogNone] = "\033[0m", /* reset */ + [eLogError] = "\033[1;31m", /* red */ + [eLogWarning] = "\033[1;33m", /* yellow */ + [eLogInfo] = "\033[1;36m", /* cyan */ + [eLogDebug] = "\033[1;34m", /* blue */ + [eNumLogLevels] = "\033[0m", /* reset */ + }; #endif #ifndef _WIN32 - /** - * @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; - switch (l) { - case eLogNone : priority = LOG_CRIT; break; - case eLogError : priority = LOG_ERR; break; - 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; - } + + /** + * @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; + switch (l) { + case eLogNone : + priority = LOG_CRIT; + break; + case eLogError : + priority = LOG_ERR; + break; + 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 - Log::Log(): - m_Destination(eLogStdout), m_MinLevel(eLogInfo), - m_LogStream (nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"), - m_IsRunning (false), m_Thread (nullptr) - { - } + Log::Log() : + m_Destination(eLogStdout), m_MinLevel(eLogInfo), + m_LogStream(nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"), + m_IsRunning(false), m_Thread(nullptr) { + } - Log::~Log () - { - delete m_Thread; - } + Log::~Log() { + delete m_Thread; + } - void Log::Start () - { - if (!m_IsRunning) - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Log::Run, this)); - } - } + void Log::Start() { + if (!m_IsRunning) { + m_IsRunning = true; + m_Thread = new std::thread(std::bind(&Log::Run, this)); + } + } - void Log::Stop () - { - switch (m_Destination) - { + void Log::Stop() { + switch (m_Destination) { #ifndef _WIN32 - case eLogSyslog : - closelog(); - break; + case eLogSyslog : + closelog(); + break; #endif - case eLogFile: - case eLogStream: - if (m_LogStream) m_LogStream->flush(); - break; - default: - /* do nothing */ - break; - } - m_IsRunning = false; - m_Queue.WakeUp (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } + case eLogFile: + case eLogStream: + if (m_LogStream) m_LogStream->flush(); + break; + default: + /* do nothing */ + break; + } + m_IsRunning = false; + m_Queue.WakeUp(); + if (m_Thread) { + m_Thread->join(); + delete m_Thread; + m_Thread = nullptr; + } + } - std::string str_tolower(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), - // static_cast(std::tolower) // wrong - // [](int c){ return std::tolower(c); } // wrong - // [](char c){ return std::tolower(c); } // wrong - [](unsigned char c){ return std::tolower(c); } // correct - ); - return s; - } + std::string str_tolower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + // static_cast(std::tolower) // wrong + // [](int c){ return std::tolower(c); } // wrong + // [](char c){ return std::tolower(c); } // wrong + [](unsigned char c) { return std::tolower(c); } // correct + ); + return s; + } - void Log::SetLogLevel (const std::string& level_) { - std::string level=str_tolower(level_); - if (level == "none") { m_MinLevel = eLogNone; } - else if (level == "error") { m_MinLevel = eLogError; } - else if (level == "warn") { m_MinLevel = eLogWarning; } - else if (level == "info") { m_MinLevel = eLogInfo; } - else if (level == "debug") { m_MinLevel = eLogDebug; } - else { - LogPrint(eLogError, "Log: Unknown loglevel: ", level); - return; - } - LogPrint(eLogInfo, "Log: Logging level set to ", level); - } + void Log::SetLogLevel(const std::string &level_) { + std::string level = str_tolower(level_); + if (level == "none") { m_MinLevel = eLogNone; } + else if (level == "error") { m_MinLevel = eLogError; } + else if (level == "warn") { m_MinLevel = eLogWarning; } + else if (level == "info") { m_MinLevel = eLogInfo; } + else if (level == "debug") { m_MinLevel = eLogDebug; } + else { + LogPrint(eLogError, "Log: Unknown loglevel: ", level); + return; + } + LogPrint(eLogInfo, "Log: Logging level set to ", level); + } - const char * Log::TimeAsString(std::time_t t) { - if (t != m_LastTimestamp) { - strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t)); - m_LastTimestamp = t; - } - return m_LastDateTime; - } + const char *Log::TimeAsString(std::time_t t) { + if (t != m_LastTimestamp) { + strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t)); + m_LastTimestamp = t; + } + return m_LastDateTime; + } - /** - * @note This function better to be run in separate thread due to disk i/o. - * Unfortunately, with current startup process with late fork() this - * will give us nothing but pain. Maybe later. See in NetDb as example. - */ - void Log::Process(std::shared_ptr msg) - { - if (!msg) return; - std::hash hasher; - unsigned short short_tid; - short_tid = (short) (hasher(msg->tid) % 1000); - switch (m_Destination) { + /** + * @note This function better to be run in separate thread due to disk i/o. + * Unfortunately, with current startup process with late fork() this + * will give us nothing but pain. Maybe later. See in NetDb as example. + */ + void Log::Process(std::shared_ptr msg) { + if (!msg) return; + std::hash hasher; + unsigned short short_tid; + short_tid = (short) (hasher(msg->tid) % 1000); + switch (m_Destination) { #ifndef _WIN32 - case eLogSyslog: - syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str()); - break; + case eLogSyslog: + syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str()); + break; #endif - case eLogFile: - case eLogStream: - if (m_LogStream) - *m_LogStream << TimeAsString(msg->timestamp) - << "@" << short_tid - << "/" << g_LogLevelStr[msg->level] - << " - " << msg->text << std::endl; - break; - case eLogStdout: - default: - std::cout << TimeAsString(msg->timestamp) - << "@" << short_tid - << "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels] - << " - " << msg->text << std::endl; - break; - } // switch - } + case eLogFile: + case eLogStream: + if (m_LogStream) + *m_LogStream << TimeAsString(msg->timestamp) + << "@" << short_tid + << "/" << g_LogLevelStr[msg->level] + << " - " << msg->text << std::endl; + break; + case eLogStdout: + default: + std::cout << TimeAsString(msg->timestamp) + << "@" << short_tid + << "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] + << LogMsgColors[eNumLogLevels] + << " - " << msg->text << std::endl; + break; + } // switch + } - void Log::Run () - { - i2p::util::SetThreadName("Logging"); + void Log::Run() { + i2p::util::SetThreadName("Logging"); - Reopen (); - while (m_IsRunning) - { - std::shared_ptr msg; - while ((msg = m_Queue.Get ())) - Process (msg); - if (m_LogStream) m_LogStream->flush(); - if (m_IsRunning) - m_Queue.Wait (); - } - } + Reopen(); + while (m_IsRunning) { + std::shared_ptr msg; + while ((msg = m_Queue.Get())) + Process(msg); + if (m_LogStream) m_LogStream->flush(); + if (m_IsRunning) + m_Queue.Wait(); + } + } - void Log::Append(std::shared_ptr & msg) - { - m_Queue.Put(msg); - } + void Log::Append(std::shared_ptr &msg) { + m_Queue.Put(msg); + } - void Log::SendTo (const std::string& path) - { - if (m_LogStream) m_LogStream = nullptr; // close previous - auto flags = std::ofstream::out | std::ofstream::app; - auto os = std::make_shared (path, flags); - if (os->is_open ()) - { - m_HasColors = false; - m_Logfile = path; - m_Destination = eLogFile; - m_LogStream = os; - return; - } - LogPrint(eLogError, "Log: Can't open file ", path); - } + void Log::SendTo(const std::string &path) { + if (m_LogStream) m_LogStream = nullptr; // close previous + auto flags = std::ofstream::out | std::ofstream::app; + auto os = std::make_shared(path, flags); + if (os->is_open()) { + m_HasColors = false; + m_Logfile = path; + m_Destination = eLogFile; + m_LogStream = os; + return; + } + LogPrint(eLogError, "Log: Can't open file ", path); + } - void Log::SendTo (std::shared_ptr os) { - m_HasColors = false; - m_Destination = eLogStream; - m_LogStream = os; - } + void Log::SendTo(std::shared_ptr os) { + m_HasColors = false; + m_Destination = eLogStream; + m_LogStream = os; + } #ifndef _WIN32 - void Log::SendTo(const char *name, int facility) { - if (m_MinLevel == eLogNone) return; - m_HasColors = false; - m_Destination = eLogSyslog; - m_LogStream = nullptr; - openlog(name, LOG_CONS | LOG_PID, facility); - } + + void Log::SendTo(const char *name, int facility) { + if (m_MinLevel == eLogNone) return; + m_HasColors = false; + m_Destination = eLogSyslog; + m_LogStream = nullptr; + openlog(name, LOG_CONS | LOG_PID, facility); + } + #endif - void Log::Reopen() { - if (m_Destination == eLogFile) - SendTo(m_Logfile); - } + void Log::Reopen() { + if (m_Destination == eLogFile) + SendTo(m_Logfile); + } - Log & Logger() { - return logger; - } + Log &Logger() { + return logger; + } - static ThrowFunction g_ThrowFunction; - ThrowFunction GetThrowFunction () { return g_ThrowFunction; } - void SetThrowFunction (ThrowFunction f) { g_ThrowFunction = f; } + static ThrowFunction g_ThrowFunction; -} // log + ThrowFunction GetThrowFunction() { return g_ThrowFunction; } + + void SetThrowFunction(ThrowFunction f) { g_ThrowFunction = f; } + + } // log } // i2p diff --git a/libi2pd/Log.h b/libi2pd/Log.h index 465e10bc..daa786a0 100644 --- a/libi2pd/Log.h +++ b/libi2pd/Log.h @@ -21,158 +21,176 @@ #include "Queue.h" #ifndef _WIN32 + #include + #endif -enum LogLevel -{ - eLogNone = 0, - eLogError, - eLogWarning, - eLogInfo, - eLogDebug, - eNumLogLevels +enum LogLevel { + eLogNone = 0, + eLogError, + eLogWarning, + eLogInfo, + eLogDebug, + eNumLogLevels }; enum LogType { - eLogStdout = 0, - eLogStream, - eLogFile, + eLogStdout = 0, + eLogStream, + eLogFile, #ifndef _WIN32 - eLogSyslog, + eLogSyslog, #endif }; namespace i2p { -namespace log { + namespace log { - struct LogMsg; /* forward declaration */ + struct LogMsg; /* forward declaration */ - class Log - { - private: + class Log { + private: - enum LogType m_Destination; - enum LogLevel m_MinLevel; - std::shared_ptr m_LogStream; - std::string m_Logfile; - std::time_t m_LastTimestamp; - char m_LastDateTime[64]; - i2p::util::Queue > m_Queue; - bool m_HasColors; - std::string m_TimeFormat; - volatile bool m_IsRunning; - std::thread * m_Thread; + enum LogType m_Destination; + enum LogLevel m_MinLevel; + std::shared_ptr m_LogStream; + std::string m_Logfile; + std::time_t m_LastTimestamp; + char m_LastDateTime[64]; + i2p::util::Queue > + m_Queue; + bool m_HasColors; + std::string m_TimeFormat; + volatile bool m_IsRunning; + std::thread *m_Thread; - private: + private: - /** prevent making copies */ - Log (const Log &); - const Log& operator=(const Log&); + /** prevent making copies */ + Log(const Log &); - void Run (); - void Process (std::shared_ptr msg); + const Log &operator=(const 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); + void Run(); - public: + void Process(std::shared_ptr 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; }; - LogLevel GetLogLevel () { return m_MinLevel; }; + public: - void Start (); - 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); + ~Log(); - /** - * @brief Sets log destination to logfile - * @param path Path to logfile - */ - void SendTo (const std::string &path); + LogType GetLogType() { return m_Destination; }; - /** - * @brief Sets log destination to given output stream - * @param os Output stream - */ - void SendTo (std::shared_ptr os); + LogLevel GetLogLevel() { return m_MinLevel; }; - /** - * @brief Sets format for timestamps in log - * @param format String with timestamp format - */ - void SetTimeFormat (std::string format) { m_TimeFormat = format; }; + void Start(); - #ifndef _WIN32 - /** - * @brief Sets log destination to syslog - * @param name Wanted program name - * @param facility Wanted log category - */ - void SendTo (const char *name, int facility); - #endif + void Stop(); - /** - * @brief Format log message and write to output stream/syslog - * @param msg Pointer to processed message - */ - void Append(std::shared_ptr &); + /** + * @brief Sets minimal allowed level for log messages + * @param level String with wanted minimal msg level + */ + 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 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 */ + /** + * @brief Sets log destination to given output stream + * @param os Output stream + */ + void SendTo(std::shared_ptr os); - 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 ThrowFunction; - ThrowFunction GetThrowFunction (); - void SetThrowFunction (ThrowFunction f); -} // log + /** + * @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 + * @param msg Pointer to processed message + */ + void Append(std::shared_ptr &); + + /** @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 ThrowFunction; + + ThrowFunction GetThrowFunction(); + + void SetThrowFunction(ThrowFunction f); + } // log } // i2p /** internal usage only -- folding args array to single string */ template -void LogPrint (std::stringstream& s, TValue&& arg) noexcept +void LogPrint(std::stringstream &s, TValue &&arg) + +noexcept { - s << std::forward(arg); +s << +std::forward(arg); } #if (__cplusplus < 201703L) // below C++ 17 + /** internal usage only -- folding args array to single string */ template -void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept +void LogPrint(std::stringstream &s, TValue &&arg, TArgs &&... args) + +noexcept { - LogPrint (s, std::forward(arg)); - LogPrint (s, std::forward(args)...); +LogPrint (s, std::forward(arg) +); +LogPrint (s, std::forward(args) +...); } #endif @@ -182,24 +200,33 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept * @param args Array of message parts */ template -void LogPrint (LogLevel level, TArgs&&... args) noexcept -{ - i2p::log::Log &log = i2p::log::Logger(); - if (level > log.GetLogLevel ()) - return; +void LogPrint(LogLevel level, TArgs &&... args) - // fold message to single string - std::stringstream ss; +noexcept +{ +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 - (LogPrint (ss, std::forward(args)), ...); +(LogPrint (ss, std::forward(args)), ...); #else - LogPrint (ss, std::forward(args)...); +LogPrint (ss, std::forward(args) +...); #endif - auto msg = std::make_shared(level, std::time(nullptr), std::move(ss).str()); - msg->tid = std::this_thread::get_id(); - log.Append(msg); +auto msg = std::make_shared(level, std::time(nullptr), std::move(ss).str()); +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 */ template -void ThrowFatal (TArgs&&... args) noexcept +void ThrowFatal(TArgs &&... args) + +noexcept { - auto f = i2p::log::GetThrowFunction (); - if (!f) return; - // fold message to single string - std::stringstream ss(""); +auto f = i2p::log::GetThrowFunction(); +if (!f) return; +// fold message to single string +std::stringstream ss(""); #if (__cplusplus >= 201703L) // C++ 17 or higher - (LogPrint (ss, std::forward(args)), ...); +(LogPrint (ss, std::forward(args)), ...); #else - LogPrint (ss, std::forward(args)...); +LogPrint (ss, std::forward(args) +...); #endif - f (ss.str ()); +f (ss +. + +str() + +); } #endif // LOG_H__ diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 8a2d6c7c..b41885a1 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -24,1723 +24,1563 @@ #include "util.h" #if defined(__linux__) && !defined(_NETINET_IN_H) - #include +#include #endif -namespace i2p -{ -namespace transport -{ - NTCP2Establisher::NTCP2Establisher (): - m_SessionConfirmedBuffer (nullptr) - { - } +namespace i2p { + namespace transport { + NTCP2Establisher::NTCP2Establisher() : + m_SessionConfirmedBuffer(nullptr) { + } - NTCP2Establisher::~NTCP2Establisher () - { - delete[] m_SessionConfirmedBuffer; - } + NTCP2Establisher::~NTCP2Establisher() { + delete[] m_SessionConfirmedBuffer; + } - void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) - { - i2p::crypto::InitNoiseXKState (*this, rs); - // h = SHA256(h || epub) - MixHash (epub, 32); - // x25519 between pub and priv - uint8_t inputKeyMaterial[32]; - priv.Agree (pub, inputKeyMaterial); - MixKey (inputKeyMaterial); - } + void + NTCP2Establisher::KeyDerivationFunction1(const uint8_t *pub, i2p::crypto::X25519Keys &priv, const uint8_t *rs, + const uint8_t *epub) { + i2p::crypto::InitNoiseXKState(*this, rs); + // h = SHA256(h || epub) + MixHash(epub, 32); + // x25519 between pub and priv + uint8_t inputKeyMaterial[32]; + priv.Agree(pub, inputKeyMaterial); + MixKey(inputKeyMaterial); + } - void NTCP2Establisher::KDF1Alice () - { - KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ()); - } + void NTCP2Establisher::KDF1Alice() { + KeyDerivationFunction1(m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub()); + } - void NTCP2Establisher::KDF1Bob () - { - KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); - } + void NTCP2Establisher::KDF1Bob() { + KeyDerivationFunction1(GetRemotePub(), i2p::context.GetNTCP2StaticKeys(), + i2p::context.GetNTCP2StaticPublicKey(), GetRemotePub()); + } - void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) - { - MixHash (sessionRequest + 32, 32); // encrypted payload + void NTCP2Establisher::KeyDerivationFunction2(const uint8_t *sessionRequest, size_t sessionRequestLen, + const uint8_t *epub) { + MixHash(sessionRequest + 32, 32); // encrypted payload - int paddingLength = sessionRequestLen - 64; - if (paddingLength > 0) - MixHash (sessionRequest + 64, paddingLength); - MixHash (epub, 32); + int paddingLength = sessionRequestLen - 64; + if (paddingLength > 0) + MixHash(sessionRequest + 64, paddingLength); + MixHash(epub, 32); - // x25519 between remote pub and ephemaral priv - uint8_t inputKeyMaterial[32]; - m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial); + // x25519 between remote pub and ephemaral priv + uint8_t inputKeyMaterial[32]; + m_EphemeralKeys->Agree(GetRemotePub(), inputKeyMaterial); - MixKey (inputKeyMaterial); - } + MixKey(inputKeyMaterial); + } - void NTCP2Establisher::KDF2Alice () - { - KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); - } + void NTCP2Establisher::KDF2Alice() { + KeyDerivationFunction2(m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub()); + } - void NTCP2Establisher::KDF2Bob () - { - KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); - } + void NTCP2Establisher::KDF2Bob() { + KeyDerivationFunction2(m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub()); + } - void NTCP2Establisher::KDF3Alice () - { - uint8_t inputKeyMaterial[32]; - i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); - MixKey (inputKeyMaterial); - } + void NTCP2Establisher::KDF3Alice() { + uint8_t inputKeyMaterial[32]; + i2p::context.GetNTCP2StaticKeys().Agree(GetRemotePub(), inputKeyMaterial); + MixKey(inputKeyMaterial); + } - void NTCP2Establisher::KDF3Bob () - { - uint8_t inputKeyMaterial[32]; - m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial); - MixKey (inputKeyMaterial); - } + void NTCP2Establisher::KDF3Bob() { + uint8_t inputKeyMaterial[32]; + m_EphemeralKeys->Agree(m_RemoteStaticKey, inputKeyMaterial); + MixKey(inputKeyMaterial); + } - void NTCP2Establisher::CreateEphemeralKey () - { - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - } + void NTCP2Establisher::CreateEphemeralKey() { + m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair(); + } - void NTCP2Establisher::CreateSessionRequestMessage () - { - // create buffer and fill padding - auto paddingLength = rand () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes - m_SessionRequestBufferLen = paddingLength + 64; - RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); - // encrypt X - i2p::crypto::CBCEncryption encryption; - encryption.SetKey (m_RemoteIdentHash); - encryption.SetIV (m_IV); - encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X - encryption.GetIV (m_IV); // save IV for SessionCreated - // encryption key for next block - KDF1Alice (); - // fill options - uint8_t options[32]; // actual options size is 16 bytes - memset (options, 0, 16); - options[0] = i2p::context.GetNetID (); // network ID - options[1] = 2; // ver - htobe16buf (options + 2, paddingLength); // padLen - // m3p2Len - auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen (); - m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options - htobe16buf (options + 4, m3p2Len); - // fill m3p2 payload (RouterInfo block) - m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; // m3p1 is 48 bytes - uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; - m3p2[0] = eNTCP2BlkRouterInfo; // block - htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI - m3p2[3] = 0; // flag - memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex - // 2 bytes reserved - htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA - // 4 bytes reserved - // sign and encrypt options, use m_H as AD - uint8_t nonce[12]; - memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt - } + void NTCP2Establisher::CreateSessionRequestMessage() { + // create buffer and fill padding + auto paddingLength = + rand() % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes + m_SessionRequestBufferLen = paddingLength + 64; + RAND_bytes(m_SessionRequestBuffer + 64, paddingLength); + // encrypt X + i2p::crypto::CBCEncryption encryption; + encryption.SetKey(m_RemoteIdentHash); + encryption.SetIV(m_IV); + encryption.Encrypt(GetPub(), 32, m_SessionRequestBuffer); // X + encryption.GetIV(m_IV); // save IV for SessionCreated + // encryption key for next block + KDF1Alice(); + // fill options + uint8_t options[32]; // actual options size is 16 bytes + memset(options, 0, 16); + options[0] = i2p::context.GetNetID(); // network ID + options[1] = 2; // ver + htobe16buf(options + 2, paddingLength); // padLen + // m3p2Len + auto bufLen = i2p::context.GetRouterInfo().GetBufferLen(); + m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options + htobe16buf(options + 4, m3p2Len); + // fill m3p2 payload (RouterInfo block) + m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; // m3p1 is 48 bytes + uint8_t *m3p2 = m_SessionConfirmedBuffer + 48; + m3p2[0] = eNTCP2BlkRouterInfo; // block + htobe16buf(m3p2 + 1, bufLen + 1); // flag + RI + m3p2[3] = 0; // flag + memcpy(m3p2 + 4, i2p::context.GetRouterInfo().GetBuffer(), + bufLen); // TODO: own RI should be protected by mutex + // 2 bytes reserved + htobe32buf(options + 8, i2p::util::GetSecondsSinceEpoch()); // tsA + // 4 bytes reserved + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset(nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305(options, 16, GetH(), 32, GetK(), nonce, m_SessionRequestBuffer + 32, 32, + true); // encrypt + } - void NTCP2Establisher::CreateSessionCreatedMessage () - { - auto paddingLen = rand () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64); - m_SessionCreatedBufferLen = paddingLen + 64; - RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); - // encrypt Y - i2p::crypto::CBCEncryption encryption; - encryption.SetKey (i2p::context.GetIdentHash ()); - encryption.SetIV (m_IV); - encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y - // encryption key for next block (m_K) - KDF2Bob (); - uint8_t options[16]; - memset (options, 0, 16); - htobe16buf (options + 2, paddingLen); // padLen - htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB - // sign and encrypt options, use m_H as AD - uint8_t nonce[12]; - memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt + void NTCP2Establisher::CreateSessionCreatedMessage() { + auto paddingLen = rand() % (NTCP2_SESSION_CREATED_MAX_SIZE - 64); + m_SessionCreatedBufferLen = paddingLen + 64; + RAND_bytes(m_SessionCreatedBuffer + 64, paddingLen); + // encrypt Y + i2p::crypto::CBCEncryption encryption; + encryption.SetKey(i2p::context.GetIdentHash()); + encryption.SetIV(m_IV); + encryption.Encrypt(GetPub(), 32, m_SessionCreatedBuffer); // Y + // encryption key for next block (m_K) + KDF2Bob(); + uint8_t options[16]; + memset(options, 0, 16); + htobe16buf(options + 2, paddingLen); // padLen + htobe32buf(options + 8, i2p::util::GetSecondsSinceEpoch()); // tsB + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset(nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305(options, 16, GetH(), 32, GetK(), nonce, m_SessionCreatedBuffer + 32, 32, + true); // encrypt - } + } - void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce) - { - // update AD - MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload - int paddingLength = m_SessionCreatedBufferLen - 64; - if (paddingLength > 0) - MixHash (m_SessionCreatedBuffer + 64, paddingLength); + void NTCP2Establisher::CreateSessionConfirmedMessagePart1(const uint8_t *nonce) { + // update AD + MixHash(m_SessionCreatedBuffer + 32, 32); // encrypted payload + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + MixHash(m_SessionCreatedBuffer + 64, paddingLength); - // part1 48 bytes - i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt - } + // part1 48 bytes + i2p::crypto::AEADChaCha20Poly1305(i2p::context.GetNTCP2StaticPublicKey(), 32, GetH(), 32, GetK(), nonce, + m_SessionConfirmedBuffer, 48, true); // encrypt + } - void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce) - { - // part 2 - // update AD again - MixHash (m_SessionConfirmedBuffer, 48); - // encrypt m3p2, it must be filled in SessionRequest - KDF3Alice (); - uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; - i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt - // update h again - MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext) - } + void NTCP2Establisher::CreateSessionConfirmedMessagePart2(const uint8_t *nonce) { + // part 2 + // update AD again + MixHash(m_SessionConfirmedBuffer, 48); + // encrypt m3p2, it must be filled in SessionRequest + KDF3Alice(); + uint8_t *m3p2 = m_SessionConfirmedBuffer + 48; + i2p::crypto::AEADChaCha20Poly1305(m3p2, m3p2Len - 16, GetH(), 32, GetK(), nonce, m3p2, m3p2Len, + true); // encrypt + // update h again + MixHash(m3p2, m3p2Len); //h = SHA256(h || ciphertext) + } - bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew) - { - clockSkew = false; - // decrypt X - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (i2p::context.GetIdentHash ()); - decryption.SetIV (i2p::context.GetNTCP2IV ()); - decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ()); - decryption.GetIV (m_IV); // save IV for SessionCreated - // decryption key for next block - KDF1Bob (); - // verify MAC and decrypt options block (32 bytes), use m_H as AD - uint8_t nonce[12], options[16]; - memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt - { - // options - if (options[0] && options[0] != i2p::context.GetNetID ()) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest networkID ", (int)options[0], " mismatch. Expected ", i2p::context.GetNetID ()); - return false; - } - if (options[1] == 2) // ver is always 2 - { - paddingLen = bufbe16toh (options + 2); - m_SessionRequestBufferLen = paddingLen + 64; - m3p2Len = bufbe16toh (options + 4); - if (m3p2Len < 16) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest m3p2len=", m3p2Len, " is too short"); - return false; - } - // check timestamp - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsA = bufbe32toh (options + 8); - if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest time difference ", (int)(ts - tsA), " exceeds clock skew"); - clockSkew = true; - // we send SessionCreate to let Alice know our time and then close session - } - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]); - return false; - } - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); - return false; - } - return true; - } + bool NTCP2Establisher::ProcessSessionRequestMessage(uint16_t &paddingLen, bool &clockSkew) { + clockSkew = false; + // decrypt X + i2p::crypto::CBCDecryption decryption; + decryption.SetKey(i2p::context.GetIdentHash()); + decryption.SetIV(i2p::context.GetNTCP2IV()); + decryption.Decrypt(m_SessionRequestBuffer, 32, GetRemotePub()); + decryption.GetIV(m_IV); // save IV for SessionCreated + // decryption key for next block + KDF1Bob(); + // verify MAC and decrypt options block (32 bytes), use m_H as AD + uint8_t nonce[12], options[16]; + memset(nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305(m_SessionRequestBuffer + 32, 16, GetH(), 32, GetK(), nonce, options, + 16, false)) // decrypt + { + // options + if (options[0] && options[0] != i2p::context.GetNetID()) { + LogPrint(eLogWarning, "NTCP2: SessionRequest networkID ", (int) options[0], " mismatch. Expected ", + i2p::context.GetNetID()); + return false; + } + if (options[1] == 2) // ver is always 2 + { + paddingLen = bufbe16toh(options + 2); + m_SessionRequestBufferLen = paddingLen + 64; + m3p2Len = bufbe16toh(options + 4); + if (m3p2Len < 16) { + LogPrint(eLogWarning, "NTCP2: SessionRequest m3p2len=", m3p2Len, " is too short"); + return false; + } + // check timestamp + auto ts = i2p::util::GetSecondsSinceEpoch(); + uint32_t tsA = bufbe32toh(options + 8); + if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW) { + LogPrint(eLogWarning, "NTCP2: SessionRequest time difference ", (int) (ts - tsA), + " exceeds clock skew"); + clockSkew = true; + // we send SessionCreate to let Alice know our time and then close session + } + } else { + LogPrint(eLogWarning, "NTCP2: SessionRequest version mismatch ", (int) options[1]); + return false; + } + } else { + LogPrint(eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); + return false; + } + return true; + } - bool NTCP2Establisher::ProcessSessionCreatedMessage (uint16_t& paddingLen) - { - m_SessionCreatedBufferLen = 64; - // decrypt Y - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (m_RemoteIdentHash); - decryption.SetIV (m_IV); - decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ()); - // decryption key for next block (m_K) - KDF2Alice (); - // decrypt and verify MAC - uint8_t payload[16]; - uint8_t nonce[12]; - memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt - { - // options - paddingLen = bufbe16toh(payload + 2); - // check timestamp - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsB = bufbe32toh (payload + 8); - if (tsB < ts - NTCP2_CLOCK_SKEW || tsB > ts + NTCP2_CLOCK_SKEW) - { - LogPrint (eLogWarning, "NTCP2: SessionCreated time difference ", (int)(ts - tsB), " exceeds clock skew"); - return false; - } - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); - return false; - } - return true; - } + bool NTCP2Establisher::ProcessSessionCreatedMessage(uint16_t &paddingLen) { + m_SessionCreatedBufferLen = 64; + // decrypt Y + i2p::crypto::CBCDecryption decryption; + decryption.SetKey(m_RemoteIdentHash); + decryption.SetIV(m_IV); + decryption.Decrypt(m_SessionCreatedBuffer, 32, GetRemotePub()); + // decryption key for next block (m_K) + KDF2Alice(); + // decrypt and verify MAC + uint8_t payload[16]; + uint8_t nonce[12]; + memset(nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305(m_SessionCreatedBuffer + 32, 16, GetH(), 32, GetK(), nonce, payload, + 16, false)) // decrypt + { + // options + paddingLen = bufbe16toh(payload + 2); + // check timestamp + auto ts = i2p::util::GetSecondsSinceEpoch(); + uint32_t tsB = bufbe32toh(payload + 8); + if (tsB < ts - NTCP2_CLOCK_SKEW || tsB > ts + NTCP2_CLOCK_SKEW) { + LogPrint(eLogWarning, "NTCP2: SessionCreated time difference ", (int) (ts - tsB), + " exceeds clock skew"); + return false; + } + } else { + LogPrint(eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); + return false; + } + return true; + } - bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce) - { - // update AD - MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload - int paddingLength = m_SessionCreatedBufferLen - 64; - if (paddingLength > 0) - MixHash (m_SessionCreatedBuffer + 64, paddingLength); + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1(const uint8_t *nonce) { + // update AD + MixHash(m_SessionCreatedBuffer + 32, 32); // encrypted payload + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + MixHash(m_SessionCreatedBuffer + 64, paddingLength); - if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); - return false; - } - return true; - } + if (!i2p::crypto::AEADChaCha20Poly1305(m_SessionConfirmedBuffer, 32, GetH(), 32, GetK(), nonce, + m_RemoteStaticKey, 32, false)) // decrypt S + { + LogPrint(eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); + return false; + } + return true; + } - bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf) - { - // update AD again - MixHash (m_SessionConfirmedBuffer, 48); + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2(const uint8_t *nonce, uint8_t *m3p2Buf) { + // update AD again + MixHash(m_SessionConfirmedBuffer, 48); - KDF3Bob (); - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt - // caclulate new h again for KDF data - MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext) - else - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); - return false; - } - return true; - } + KDF3Bob(); + if (i2p::crypto::AEADChaCha20Poly1305(m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH(), 32, GetK(), + nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt + // caclulate new h again for KDF data + MixHash(m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext) + else { + LogPrint(eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); + return false; + } + return true; + } - NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter, - std::shared_ptr addr): - TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), - m_Server (server), m_Socket (m_Server.GetService ()), - m_IsEstablished (false), m_IsTerminated (false), - m_Establisher (new NTCP2Establisher), + NTCP2Session::NTCP2Session(NTCP2Server &server, std::shared_ptr in_RemoteRouter, + std::shared_ptr addr) : + TransportSession(in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), + m_Server(server), m_Socket(m_Server.GetService()), + m_IsEstablished(false), m_IsTerminated(false), + m_Establisher(new NTCP2Establisher), #if OPENSSL_SIPHASH - m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr), + m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr), #else - m_SendSipKey (nullptr), m_ReceiveSipKey (nullptr), + m_SendSipKey(nullptr), m_ReceiveSipKey(nullptr), #endif - m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), - m_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), - m_IsSending (false), m_IsReceiving (false), m_NextPaddingSize (16) - { - if (in_RemoteRouter) // Alice - { - m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash (); - if (addr) - { - memcpy (m_Establisher->m_RemoteStaticKey, addr->s, 32); - memcpy (m_Establisher->m_IV, addr->i, 16); - m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port); - } - else - LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address"); - } - m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL + - rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; - } + m_NextReceivedLen(0), m_NextReceivedBuffer(nullptr), m_NextSendBuffer(nullptr), + m_NextReceivedBufferSize(0), m_ReceiveSequenceNumber(0), m_SendSequenceNumber(0), + m_IsSending(false), m_IsReceiving(false), m_NextPaddingSize(16) { + if (in_RemoteRouter) // Alice + { + m_Establisher->m_RemoteIdentHash = GetRemoteIdentity()->GetIdentHash(); + if (addr) { + memcpy(m_Establisher->m_RemoteStaticKey, addr->s, 32); + memcpy(m_Establisher->m_IV, addr->i, 16); + m_RemoteEndpoint = boost::asio::ip::tcp::endpoint(addr->host, addr->port); + } else + LogPrint(eLogWarning, "NTCP2: Missing NTCP2 address"); + } + m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch() + NTCP2_ROUTERINFO_RESEND_INTERVAL + + rand() % NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; + } - NTCP2Session::~NTCP2Session () - { - delete[] m_NextReceivedBuffer; - delete[] m_NextSendBuffer; + NTCP2Session::~NTCP2Session() { + delete[] m_NextReceivedBuffer; + delete[] m_NextSendBuffer; #if OPENSSL_SIPHASH - if (m_SendMDCtx) EVP_MD_CTX_destroy (m_SendMDCtx); - if (m_ReceiveMDCtx) EVP_MD_CTX_destroy (m_ReceiveMDCtx); + if (m_SendMDCtx) EVP_MD_CTX_destroy (m_SendMDCtx); + if (m_ReceiveMDCtx) EVP_MD_CTX_destroy (m_ReceiveMDCtx); #endif - } + } - void NTCP2Session::Terminate () - { - if (!m_IsTerminated) - { - m_IsTerminated = true; - m_IsEstablished = false; - boost::system::error_code ec; - m_Socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (ec) - LogPrint (eLogDebug, "NTCP2: Couldn't shutdown socket: ", ec.message ()); - m_Socket.close (); - transports.PeerDisconnected (shared_from_this ()); - m_Server.RemoveNTCP2Session (shared_from_this ()); - m_SendQueue.clear (); - LogPrint (eLogDebug, "NTCP2: Session terminated"); - } - } + void NTCP2Session::Terminate() { + if (!m_IsTerminated) { + m_IsTerminated = true; + m_IsEstablished = false; + boost::system::error_code ec; + m_Socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (ec) + LogPrint(eLogDebug, "NTCP2: Couldn't shutdown socket: ", ec.message()); + m_Socket.close(); + transports.PeerDisconnected(shared_from_this()); + m_Server.RemoveNTCP2Session(shared_from_this()); + m_SendQueue.clear(); + LogPrint(eLogDebug, "NTCP2: Session terminated"); + } + } - void NTCP2Session::TerminateByTimeout () - { - SendTerminationAndTerminate (eNTCP2IdleTimeout); - } + void NTCP2Session::TerminateByTimeout() { + SendTerminationAndTerminate(eNTCP2IdleTimeout); + } - void NTCP2Session::Done () - { - m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } + void NTCP2Session::Done() { + m_Server.GetService().post(std::bind(&NTCP2Session::Terminate, shared_from_this())); + } - void NTCP2Session::Established () - { - m_IsEstablished = true; - m_Establisher.reset (nullptr); - SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT); - transports.PeerConnected (shared_from_this ()); - } + void NTCP2Session::Established() { + m_IsEstablished = true; + m_Establisher.reset(nullptr); + SetTerminationTimeout(NTCP2_TERMINATION_TIMEOUT); + transports.PeerConnected(shared_from_this()); + } - void NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } + void NTCP2Session::CreateNonce(uint64_t seqn, uint8_t *nonce) { + memset(nonce, 0, 4); + htole64buf(nonce + 4, seqn); + } - void NTCP2Session::CreateNextReceivedBuffer (size_t size) - { - if (m_NextReceivedBuffer) - { - if (size <= m_NextReceivedBufferSize) - return; // buffer is good, do nothing - else - delete[] m_NextReceivedBuffer; - } - m_NextReceivedBuffer = new uint8_t[size]; - m_NextReceivedBufferSize = size; - } + void NTCP2Session::CreateNextReceivedBuffer(size_t size) { + if (m_NextReceivedBuffer) { + if (size <= m_NextReceivedBufferSize) + return; // buffer is good, do nothing + else + delete[] m_NextReceivedBuffer; + } + m_NextReceivedBuffer = new uint8_t[size]; + m_NextReceivedBufferSize = size; + } - void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts) - { - if (m_NextReceivedBuffer && !m_IsReceiving && - ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) - { - delete[] m_NextReceivedBuffer; - m_NextReceivedBuffer = nullptr; - m_NextReceivedBufferSize = 0; - } - } + void NTCP2Session::DeleteNextReceiveBuffer(uint64_t ts) { + if (m_NextReceivedBuffer && !m_IsReceiving && + ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) { + delete[] m_NextReceivedBuffer; + m_NextReceivedBuffer = nullptr; + m_NextReceivedBufferSize = 0; + } + } - void NTCP2Session::KeyDerivationFunctionDataPhase () - { - uint8_t k[64]; - i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "", k); // k_ab, k_ba = HKDF(ck, zerolen) - memcpy (m_Kab, k, 32); memcpy (m_Kba, k + 32, 32); - uint8_t master[32]; - i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "ask", master, 32); // ask_master = HKDF(ck, zerolen, info="ask") - uint8_t h[39]; - memcpy (h, m_Establisher->GetH (), 32); - memcpy (h + 32, "siphash", 7); - i2p::crypto::HKDF (master, h, 39, "", master, 32); // sip_master = HKDF(ask_master, h || "siphash") - i2p::crypto::HKDF (master, nullptr, 0, "", k); // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen) - memcpy (m_Sipkeysab, k, 32); memcpy (m_Sipkeysba, k + 32, 32); - } + void NTCP2Session::KeyDerivationFunctionDataPhase() { + uint8_t k[64]; + i2p::crypto::HKDF(m_Establisher->GetCK(), nullptr, 0, "", k); // k_ab, k_ba = HKDF(ck, zerolen) + memcpy(m_Kab, k, 32); + memcpy(m_Kba, k + 32, 32); + uint8_t master[32]; + i2p::crypto::HKDF(m_Establisher->GetCK(), nullptr, 0, "ask", master, + 32); // ask_master = HKDF(ck, zerolen, info="ask") + uint8_t h[39]; + memcpy(h, m_Establisher->GetH(), 32); + memcpy(h + 32, "siphash", 7); + i2p::crypto::HKDF(master, h, 39, "", master, 32); // sip_master = HKDF(ask_master, h || "siphash") + i2p::crypto::HKDF(master, nullptr, 0, "", k); // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen) + memcpy(m_Sipkeysab, k, 32); + memcpy(m_Sipkeysba, k + 32, 32); + } - void NTCP2Session::SendSessionRequest () - { - m_Establisher->CreateSessionRequestMessage (); - // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void NTCP2Session::SendSessionRequest() { + m_Establisher->CreateSessionRequestMessage(); + // send message + boost::asio::async_write(m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, + m_Establisher->m_SessionRequestBufferLen), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void NTCP2Session::HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: Couldn't send SessionRequest message: ", ecode.message ()); - Terminate (); - } - else - { - // we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } + void + NTCP2Session::HandleSessionRequestSent(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + (void) bytes_transferred; + if (ecode) { + LogPrint(eLogWarning, "NTCP2: Couldn't send SessionRequest message: ", ecode.message()); + Terminate(); + } else { + // we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first + boost::asio::async_read(m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + } - void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred); - uint16_t paddingLen = 0; - bool clockSkew = false; - if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew)) - { - if (clockSkew) - { - // we don't care about padding, send SessionCreated and close session - SendSessionCreated (); - m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - else if (paddingLen > 0) - { - if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); - Terminate (); - } - } - else - SendSessionCreated (); - } - else - Terminate (); - } - } + void NTCP2Session::HandleSessionRequestReceived(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + (void) bytes_transferred; + if (ecode) { + LogPrint(eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message()); + Terminate(); + } else { + LogPrint(eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred); + uint16_t paddingLen = 0; + bool clockSkew = false; + if (m_Establisher->ProcessSessionRequestMessage(paddingLen, clockSkew)) { + if (clockSkew) { + // we don't care about padding, send SessionCreated and close session + SendSessionCreated(); + m_Server.GetService().post(std::bind(&NTCP2Session::Terminate, shared_from_this())); + } else if (paddingLen > 0) { + if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max + { + boost::asio::async_read(m_Socket, + boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, + paddingLen), boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, + shared_from_this(), std::placeholders::_1, + std::placeholders::_2)); + } else { + LogPrint(eLogWarning, "NTCP2: SessionRequest padding length ", (int) paddingLen, + " is too long"); + Terminate(); + } + } else + SendSessionCreated(); + } else + Terminate(); + } + } - void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest padding read error: ", ecode.message ()); - Terminate (); - } - else - SendSessionCreated (); - } + void NTCP2Session::HandleSessionRequestPaddingReceived(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogWarning, "NTCP2: SessionRequest padding read error: ", ecode.message()); + Terminate(); + } else + SendSessionCreated(); + } - void NTCP2Session::SendSessionCreated () - { - m_Establisher->CreateSessionCreatedMessage (); - // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void NTCP2Session::SendSessionCreated() { + m_Establisher->CreateSessionCreatedMessage(); + // send message + boost::asio::async_write(m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, + m_Establisher->m_SessionCreatedBufferLen), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void NTCP2Session::HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: SessionCreated read error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred); - uint16_t paddingLen = 0; - if (m_Establisher->ProcessSessionCreatedMessage (paddingLen)) - { - if (paddingLen > 0) - { - if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); - Terminate (); - } - } - else - SendSessionConfirmed (); - } - else - Terminate (); - } - } + void NTCP2Session::HandleSessionCreatedReceived(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogWarning, "NTCP2: SessionCreated read error: ", ecode.message()); + Terminate(); + } else { + LogPrint(eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred); + uint16_t paddingLen = 0; + if (m_Establisher->ProcessSessionCreatedMessage(paddingLen)) { + if (paddingLen > 0) { + if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max + { + boost::asio::async_read(m_Socket, + boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, + paddingLen), boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, + shared_from_this(), std::placeholders::_1, + std::placeholders::_2)); + } else { + LogPrint(eLogWarning, "NTCP2: SessionCreated padding length ", (int) paddingLen, + " is too long"); + Terminate(); + } + } else + SendSessionConfirmed(); + } else + Terminate(); + } + } - void NTCP2Session::HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: SessionCreated padding read error: ", ecode.message ()); - Terminate (); - } - else - { - m_Establisher->m_SessionCreatedBufferLen += bytes_transferred; - SendSessionConfirmed (); - } - } + void NTCP2Session::HandleSessionCreatedPaddingReceived(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogWarning, "NTCP2: SessionCreated padding read error: ", ecode.message()); + Terminate(); + } else { + m_Establisher->m_SessionCreatedBufferLen += bytes_transferred; + SendSessionConfirmed(); + } + } - void NTCP2Session::SendSessionConfirmed () - { - uint8_t nonce[12]; - CreateNonce (1, nonce); // set nonce to 1 - m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); - memset (nonce, 0, 12); // set nonce back to 0 - m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); - // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void NTCP2Session::SendSessionConfirmed() { + uint8_t nonce[12]; + CreateNonce(1, nonce); // set nonce to 1 + m_Establisher->CreateSessionConfirmedMessagePart1(nonce); + memset(nonce, 0, 12); // set nonce back to 0 + m_Establisher->CreateSessionConfirmedMessagePart2(nonce); + // send message + boost::asio::async_write(m_Socket, boost::asio::buffer(m_Establisher->m_SessionConfirmedBuffer, + m_Establisher->m3p2Len + 48), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void NTCP2Session::HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: Couldn't send SessionConfirmed message: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent"); - KeyDerivationFunctionDataPhase (); - // Alice data phase keys - m_SendKey = m_Kab; - m_ReceiveKey = m_Kba; - SetSipKeys (m_Sipkeysab, m_Sipkeysba); - memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8); - memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8); - Established (); - ReceiveLength (); + void NTCP2Session::HandleSessionConfirmedSent(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + (void) bytes_transferred; + if (ecode) { + LogPrint(eLogWarning, "NTCP2: Couldn't send SessionConfirmed message: ", ecode.message()); + Terminate(); + } else { + LogPrint(eLogDebug, "NTCP2: SessionConfirmed sent"); + KeyDerivationFunctionDataPhase(); + // Alice data phase keys + m_SendKey = m_Kab; + m_ReceiveKey = m_Kba; + SetSipKeys(m_Sipkeysab, m_Sipkeysba); + memcpy(m_ReceiveIV.buf, m_Sipkeysba + 16, 8); + memcpy(m_SendIV.buf, m_Sipkeysab + 16, 8); + Established(); + ReceiveLength(); - // TODO: remove - // m_SendQueue.push_back (CreateDeliveryStatusMsg (1)); - // SendQueue (); - } - } + // TODO: remove + // m_SendQueue.push_back (CreateDeliveryStatusMsg (1)); + // SendQueue (); + } + } - void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: Couldn't send SessionCreated message: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); - m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionConfirmedReceived , shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } + void + NTCP2Session::HandleSessionCreatedSent(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + (void) bytes_transferred; + if (ecode) { + LogPrint(eLogWarning, "NTCP2: Couldn't send SessionCreated message: ", ecode.message()); + Terminate(); + } else { + LogPrint(eLogDebug, "NTCP2: SessionCreated sent"); + m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; + boost::asio::async_read(m_Socket, boost::asio::buffer(m_Establisher->m_SessionConfirmedBuffer, + m_Establisher->m3p2Len + 48), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionConfirmedReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + } - void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP2: SessionConfirmed received"); - // part 1 - uint8_t nonce[12]; - CreateNonce (1, nonce); - if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce)) - { - // part 2 - std::vector buf(m_Establisher->m3p2Len - 16); // -MAC - memset (nonce, 0, 12); // set nonce to 0 again - if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ())) - { - KeyDerivationFunctionDataPhase (); - // Bob data phase keys - m_SendKey = m_Kba; - m_ReceiveKey = m_Kab; - SetSipKeys (m_Sipkeysba, m_Sipkeysab); - memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); - memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); - // payload - // process RI - if (buf[0] != eNTCP2BlkRouterInfo) - { - LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)buf[0], " in SessionConfirmed"); - Terminate (); - return; - } - auto size = bufbe16toh (buf.data () + 1); - if (size > buf.size () - 3) - { - LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); - Terminate (); - return; - } - // TODO: check flag - i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag - if (ri.IsUnreachable ()) - { - LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); - return; - } - if (i2p::util::GetMillisecondsSinceEpoch () > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes - { - LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2Message3Error); - return; - } - auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey); - if (!addr) - { - LogPrint (eLogError, "NTCP2: No NTCP2 address with static key found in SessionConfirmed"); - Terminate (); - return; - } - i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice - // TODO: process options + void NTCP2Session::HandleSessionConfirmedReceived(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message()); + Terminate(); + } else { + LogPrint(eLogDebug, "NTCP2: SessionConfirmed received"); + // part 1 + uint8_t nonce[12]; + CreateNonce(1, nonce); + if (m_Establisher->ProcessSessionConfirmedMessagePart1(nonce)) { + // part 2 + std::vector buf(m_Establisher->m3p2Len - 16); // -MAC + memset(nonce, 0, 12); // set nonce to 0 again + if (m_Establisher->ProcessSessionConfirmedMessagePart2(nonce, buf.data())) { + KeyDerivationFunctionDataPhase(); + // Bob data phase keys + m_SendKey = m_Kba; + m_ReceiveKey = m_Kab; + SetSipKeys(m_Sipkeysba, m_Sipkeysab); + memcpy(m_ReceiveIV.buf, m_Sipkeysab + 16, 8); + memcpy(m_SendIV.buf, m_Sipkeysba + 16, 8); + // payload + // process RI + if (buf[0] != eNTCP2BlkRouterInfo) { + LogPrint(eLogWarning, "NTCP2: Unexpected block ", (int) buf[0], " in SessionConfirmed"); + Terminate(); + return; + } + auto size = bufbe16toh(buf.data() + 1); + if (size > buf.size() - 3) { + LogPrint(eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); + Terminate(); + return; + } + // TODO: check flag + i2p::data::RouterInfo ri(buf.data() + 4, + size - 1); // 1 byte block type + 2 bytes size + 1 byte flag + if (ri.IsUnreachable()) { + LogPrint(eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); + SendTerminationAndTerminate(eNTCP2RouterInfoSignatureVerificationFail); + return; + } + if (i2p::util::GetMillisecondsSinceEpoch() > + ri.GetTimestamp() + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT * 1000LL) // 90 minutes + { + LogPrint(eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); + SendTerminationAndTerminate(eNTCP2Message3Error); + return; + } + auto addr = ri.GetNTCP2AddressWithStaticKey(m_Establisher->m_RemoteStaticKey); + if (!addr) { + LogPrint(eLogError, "NTCP2: No NTCP2 address with static key found in SessionConfirmed"); + Terminate(); + return; + } + i2p::data::netdb.PostI2NPMsg(CreateI2NPMessage(eI2NPDummyMsg, buf.data() + 3, + size)); // TODO: should insert ri and not parse it twice + // TODO: process options - // ready to communicate - auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already - SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ()); - if (m_Server.AddNTCP2Session (shared_from_this (), true)) - { - Established (); - ReceiveLength (); - } - else - Terminate (); - } - else - Terminate (); - } - else - Terminate (); - } - } + // ready to communicate + auto existing = i2p::data::netdb.FindRouter( + ri.GetRouterIdentity()->GetIdentHash()); // check if exists already + SetRemoteIdentity(existing ? existing->GetRouterIdentity() : ri.GetRouterIdentity()); + if (m_Server.AddNTCP2Session(shared_from_this(), true)) { + Established(); + ReceiveLength(); + } else + Terminate(); + } else + Terminate(); + } else + Terminate(); + } + } - void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey) - { + void NTCP2Session::SetSipKeys(const uint8_t *sendSipKey, const uint8_t *receiveSipKey) { #if OPENSSL_SIPHASH - EVP_PKEY * sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, sendSipKey, 16); - m_SendMDCtx = EVP_MD_CTX_create (); - EVP_PKEY_CTX *ctx = nullptr; - EVP_DigestSignInit (m_SendMDCtx, &ctx, nullptr, nullptr, sipKey); - EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); - EVP_PKEY_free (sipKey); + EVP_PKEY * sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, sendSipKey, 16); + m_SendMDCtx = EVP_MD_CTX_create (); + EVP_PKEY_CTX *ctx = nullptr; + EVP_DigestSignInit (m_SendMDCtx, &ctx, nullptr, nullptr, sipKey); + EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); + EVP_PKEY_free (sipKey); - sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, receiveSipKey, 16); - m_ReceiveMDCtx = EVP_MD_CTX_create (); - ctx = nullptr; - EVP_DigestSignInit (m_ReceiveMDCtx, &ctx, NULL, NULL, sipKey); - EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); - EVP_PKEY_free (sipKey); + sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, receiveSipKey, 16); + m_ReceiveMDCtx = EVP_MD_CTX_create (); + ctx = nullptr; + EVP_DigestSignInit (m_ReceiveMDCtx, &ctx, NULL, NULL, sipKey); + EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); + EVP_PKEY_free (sipKey); #else - m_SendSipKey = sendSipKey; - m_ReceiveSipKey = receiveSipKey; + m_SendSipKey = sendSipKey; + m_ReceiveSipKey = receiveSipKey; #endif - } + } - void NTCP2Session::ClientLogin () - { - m_Establisher->CreateEphemeralKey (); - SendSessionRequest (); - } + void NTCP2Session::ClientLogin() { + m_Establisher->CreateEphemeralKey(); + SendSessionRequest(); + } - void NTCP2Session::ServerLogin () - { - m_Establisher->CreateEphemeralKey (); - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } + void NTCP2Session::ServerLogin() { + m_Establisher->CreateEphemeralKey(); + boost::asio::async_read(m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void NTCP2Session::ReceiveLength () - { - if (IsTerminated ()) return; + void NTCP2Session::ReceiveLength() { + if (IsTerminated()) return; #ifdef __linux__ - const int one = 1; - setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); + const int one = 1; + setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); #endif - boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + boost::asio::async_read(m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void NTCP2Session::HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: Receive length read error: ", ecode.message ()); - Terminate (); - } - else - { + void NTCP2Session::HandleReceivedLength(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + if (ecode != boost::asio::error::operation_aborted) + LogPrint(eLogWarning, "NTCP2: Receive length read error: ", ecode.message()); + Terminate(); + } else { #if OPENSSL_SIPHASH - EVP_DigestSignInit (m_ReceiveMDCtx, nullptr, nullptr, nullptr, nullptr); - EVP_DigestSignUpdate (m_ReceiveMDCtx, m_ReceiveIV.buf, 8); - size_t l = 8; - EVP_DigestSignFinal (m_ReceiveMDCtx, m_ReceiveIV.buf, &l); + EVP_DigestSignInit (m_ReceiveMDCtx, nullptr, nullptr, nullptr, nullptr); + EVP_DigestSignUpdate (m_ReceiveMDCtx, m_ReceiveIV.buf, 8); + size_t l = 8; + EVP_DigestSignFinal (m_ReceiveMDCtx, m_ReceiveIV.buf, &l); #else - i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); + i2p::crypto::Siphash<8>(m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); #endif - // m_NextReceivedLen comes from the network in BigEndian - m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); - LogPrint (eLogDebug, "NTCP2: Received length ", m_NextReceivedLen); - if (m_NextReceivedLen >= 16) - { - CreateNextReceivedBuffer (m_NextReceivedLen); - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - if (!ec && moreBytes >= m_NextReceivedLen) - { - // read and process message immediately if available - moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec); - HandleReceived (ec, moreBytes); - } - else - Receive (); - } - else - { - LogPrint (eLogError, "NTCP2: Received length ", m_NextReceivedLen, " is too short"); - Terminate (); - } - } - } + // m_NextReceivedLen comes from the network in BigEndian + m_NextReceivedLen = be16toh(m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); + LogPrint(eLogDebug, "NTCP2: Received length ", m_NextReceivedLen); + if (m_NextReceivedLen >= 16) { + CreateNextReceivedBuffer(m_NextReceivedLen); + boost::system::error_code ec; + size_t moreBytes = m_Socket.available(ec); + if (!ec && moreBytes >= m_NextReceivedLen) { + // read and process message immediately if available + moreBytes = boost::asio::read(m_Socket, + boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), + boost::asio::transfer_all(), ec); + HandleReceived(ec, moreBytes); + } else + Receive(); + } else { + LogPrint(eLogError, "NTCP2: Received length ", m_NextReceivedLen, " is too short"); + Terminate(); + } + } + } - void NTCP2Session::Receive () - { - if (IsTerminated ()) return; + void NTCP2Session::Receive() { + if (IsTerminated()) return; #ifdef __linux__ - const int one = 1; - setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); + const int one = 1; + setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); #endif - m_IsReceiving = true; - boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + m_IsReceiving = true; + boost::asio::async_read(m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleReceived, shared_from_this(), std::placeholders::_1, + std::placeholders::_2)); + } - void NTCP2Session::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); - Terminate (); - } - else - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumReceivedBytes += bytes_transferred + 2; // + length - i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); - uint8_t nonce[12]; - CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; - if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) - { - LogPrint (eLogDebug, "NTCP2: Received message decrypted"); - ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); - m_IsReceiving = false; - ReceiveLength (); - } - else - { - LogPrint (eLogWarning, "NTCP2: Received AEAD verification failed "); - SendTerminationAndTerminate (eNTCP2DataPhaseAEADFailure); - } - } - } + void NTCP2Session::HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + if (ecode != boost::asio::error::operation_aborted) + LogPrint(eLogWarning, "NTCP2: Receive read error: ", ecode.message()); + Terminate(); + } else { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + m_NumReceivedBytes += bytes_transferred + 2; // + length + i2p::transport::transports.UpdateReceivedBytes(bytes_transferred); + uint8_t nonce[12]; + CreateNonce(m_ReceiveSequenceNumber, nonce); + m_ReceiveSequenceNumber++; + if (i2p::crypto::AEADChaCha20Poly1305(m_NextReceivedBuffer, m_NextReceivedLen - 16, nullptr, 0, + m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, + false)) { + LogPrint(eLogDebug, "NTCP2: Received message decrypted"); + ProcessNextFrame(m_NextReceivedBuffer, m_NextReceivedLen - 16); + m_IsReceiving = false; + ReceiveLength(); + } else { + LogPrint(eLogWarning, "NTCP2: Received AEAD verification failed "); + SendTerminationAndTerminate(eNTCP2DataPhaseAEADFailure); + } + } + } - void NTCP2Session::ProcessNextFrame (const uint8_t * frame, size_t len) - { - size_t offset = 0; - while (offset < len) - { - uint8_t blk = frame[offset]; - offset++; - auto size = bufbe16toh (frame + offset); - offset += 2; - LogPrint (eLogDebug, "NTCP2: Block type ", (int)blk, " of size ", size); - if (size > len) - { - LogPrint (eLogError, "NTCP2: Unexpected block length ", size); - break; - } - switch (blk) - { - case eNTCP2BlkDateTime: - LogPrint (eLogDebug, "NTCP2: Datetime"); - break; - case eNTCP2BlkOptions: - LogPrint (eLogDebug, "NTCP2: Options"); - break; - case eNTCP2BlkRouterInfo: - { - LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]); - i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, frame + offset, size)); - break; - } - case eNTCP2BlkI2NPMessage: - { - LogPrint (eLogDebug, "NTCP2: I2NP"); - if (size > I2NP_MAX_MESSAGE_SIZE) - { - LogPrint (eLogError, "NTCP2: I2NP block is too long ", size); - break; - } - auto nextMsg = (frame[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPMessage (size); - nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header - if (nextMsg->len <= nextMsg->maxLen) - { - memcpy (nextMsg->GetNTCP2Header (), frame + offset, size); - nextMsg->FromNTCP2 (); - m_Handler.PutNextMessage (std::move (nextMsg)); - } - else - LogPrint (eLogError, "NTCP2: I2NP block is too long for I2NP message"); - break; - } - case eNTCP2BlkTermination: - if (size >= 9) - { - LogPrint (eLogDebug, "NTCP2: Termination. reason=", (int)(frame[offset + 8])); - Terminate (); - } - else - LogPrint (eLogWarning, "NTCP2: Unexpected termination block size ", size); - break; - case eNTCP2BlkPadding: - LogPrint (eLogDebug, "NTCP2: Padding"); - break; - default: - LogPrint (eLogWarning, "NTCP2: Unknown block type ", (int)blk); - } - offset += size; - } - m_Handler.Flush (); - } + void NTCP2Session::ProcessNextFrame(const uint8_t *frame, size_t len) { + size_t offset = 0; + while (offset < len) { + uint8_t blk = frame[offset]; + offset++; + auto size = bufbe16toh(frame + offset); + offset += 2; + LogPrint(eLogDebug, "NTCP2: Block type ", (int) blk, " of size ", size); + if (size > len) { + LogPrint(eLogError, "NTCP2: Unexpected block length ", size); + break; + } + switch (blk) { + case eNTCP2BlkDateTime: + LogPrint(eLogDebug, "NTCP2: Datetime"); + break; + case eNTCP2BlkOptions: + LogPrint(eLogDebug, "NTCP2: Options"); + break; + case eNTCP2BlkRouterInfo: { + LogPrint(eLogDebug, "NTCP2: RouterInfo flag=", (int) frame[offset]); + i2p::data::netdb.PostI2NPMsg(CreateI2NPMessage(eI2NPDummyMsg, frame + offset, size)); + break; + } + case eNTCP2BlkI2NPMessage: { + LogPrint(eLogDebug, "NTCP2: I2NP"); + if (size > I2NP_MAX_MESSAGE_SIZE) { + LogPrint(eLogError, "NTCP2: I2NP block is too long ", size); + break; + } + auto nextMsg = (frame[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage(true) : NewI2NPMessage( + size); + nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header + if (nextMsg->len <= nextMsg->maxLen) { + memcpy(nextMsg->GetNTCP2Header(), frame + offset, size); + nextMsg->FromNTCP2(); + m_Handler.PutNextMessage(std::move(nextMsg)); + } else + LogPrint(eLogError, "NTCP2: I2NP block is too long for I2NP message"); + break; + } + case eNTCP2BlkTermination: + if (size >= 9) { + LogPrint(eLogDebug, "NTCP2: Termination. reason=", (int) (frame[offset + 8])); + Terminate(); + } else + LogPrint(eLogWarning, "NTCP2: Unexpected termination block size ", size); + break; + case eNTCP2BlkPadding: + LogPrint(eLogDebug, "NTCP2: Padding"); + break; + default: + LogPrint(eLogWarning, "NTCP2: Unknown block type ", (int) blk); + } + offset += size; + } + m_Handler.Flush(); + } - void NTCP2Session::SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf) - { + void NTCP2Session::SetNextSentFrameLength(size_t frameLen, uint8_t *lengthBuf) { #if OPENSSL_SIPHASH - EVP_DigestSignInit (m_SendMDCtx, nullptr, nullptr, nullptr, nullptr); - EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); - size_t l = 8; - EVP_DigestSignFinal (m_SendMDCtx, m_SendIV.buf, &l); + EVP_DigestSignInit (m_SendMDCtx, nullptr, nullptr, nullptr, nullptr); + EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); + size_t l = 8; + EVP_DigestSignFinal (m_SendMDCtx, m_SendIV.buf, &l); #else - i2p::crypto::Siphash<8> (m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); + i2p::crypto::Siphash<8>(m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); #endif - // length must be in BigEndian - htobe16buf (lengthBuf, frameLen ^ le16toh (m_SendIV.key)); - LogPrint (eLogDebug, "NTCP2: Sent length ", frameLen); - } + // length must be in BigEndian + htobe16buf(lengthBuf, frameLen ^ le16toh (m_SendIV.key)); + LogPrint(eLogDebug, "NTCP2: Sent length ", frameLen); + } - void NTCP2Session::SendI2NPMsgs (std::vector >& msgs) - { - if (msgs.empty () || IsTerminated ()) return; + void NTCP2Session::SendI2NPMsgs(std::vector > &msgs) { + if (msgs.empty() || IsTerminated()) return; - size_t totalLen = 0; - std::vector > encryptBufs; - std::vector bufs; - std::shared_ptr first; - uint8_t * macBuf = nullptr; - for (auto& it: msgs) - { - it->ToNTCP2 (); - auto buf = it->GetNTCP2Header (); - auto len = it->GetNTCP2Length (); - // block header - buf -= 3; - buf[0] = eNTCP2BlkI2NPMessage; // blk - htobe16buf (buf + 1, len); // size - len += 3; - totalLen += len; - encryptBufs.push_back ( {buf, len} ); - if (&it == &msgs.front ()) // first message - { - // allocate two bytes for length - buf -= 2; len += 2; - first = it; - } - if (&it == &msgs.back () && it->len + 16 < it->maxLen) // last message - { - // if it's long enough we add padding and MAC to it - // create padding block - auto paddingLen = CreatePaddingBlock (totalLen, buf + len, it->maxLen - it->len - 16); - if (paddingLen) - { - encryptBufs.push_back ( {buf + len, paddingLen} ); - len += paddingLen; - totalLen += paddingLen; - } - macBuf = buf + len; - // allocate 16 bytes for MAC - len += 16; - } + size_t totalLen = 0; + std::vector > encryptBufs; + std::vector bufs; + std::shared_ptr first; + uint8_t *macBuf = nullptr; + for (auto &it: msgs) { + it->ToNTCP2(); + auto buf = it->GetNTCP2Header(); + auto len = it->GetNTCP2Length(); + // block header + buf -= 3; + buf[0] = eNTCP2BlkI2NPMessage; // blk + htobe16buf(buf + 1, len); // size + len += 3; + totalLen += len; + encryptBufs.push_back({buf, len}); + if (&it == &msgs.front()) // first message + { + // allocate two bytes for length + buf -= 2; + len += 2; + first = it; + } + if (&it == &msgs.back() && it->len + 16 < it->maxLen) // last message + { + // if it's long enough we add padding and MAC to it + // create padding block + auto paddingLen = CreatePaddingBlock(totalLen, buf + len, it->maxLen - it->len - 16); + if (paddingLen) { + encryptBufs.push_back({buf + len, paddingLen}); + len += paddingLen; + totalLen += paddingLen; + } + macBuf = buf + len; + // allocate 16 bytes for MAC + len += 16; + } - bufs.push_back (boost::asio::buffer (buf, len)); - } + bufs.push_back(boost::asio::buffer(buf, len)); + } - if (!macBuf) // last block was not enough for MAC - { - // allocate send buffer - m_NextSendBuffer = new uint8_t[287]; // can be any size > 16, we just allocate 287 frequently - // create padding block - auto paddingLen = CreatePaddingBlock (totalLen, m_NextSendBuffer, 287 - 16); - // and padding block to encrypt and send - if (paddingLen) - encryptBufs.push_back ( {m_NextSendBuffer, paddingLen} ); - bufs.push_back (boost::asio::buffer (m_NextSendBuffer, paddingLen + 16)); - macBuf = m_NextSendBuffer + paddingLen; - totalLen += paddingLen; - } - uint8_t nonce[12]; - CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers - SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block + if (!macBuf) // last block was not enough for MAC + { + // allocate send buffer + m_NextSendBuffer = new uint8_t[287]; // can be any size > 16, we just allocate 287 frequently + // create padding block + auto paddingLen = CreatePaddingBlock(totalLen, m_NextSendBuffer, 287 - 16); + // and padding block to encrypt and send + if (paddingLen) + encryptBufs.push_back({m_NextSendBuffer, paddingLen}); + bufs.push_back(boost::asio::buffer(m_NextSendBuffer, paddingLen + 16)); + macBuf = m_NextSendBuffer + paddingLen; + totalLen += paddingLen; + } + uint8_t nonce[12]; + CreateNonce(m_SendSequenceNumber, nonce); + m_SendSequenceNumber++; + i2p::crypto::AEADChaCha20Poly1305Encrypt(encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers + SetNextSentFrameLength(totalLen + 16, first->GetNTCP2Header() - 5); // frame length right before first block - // send buffers - m_IsSending = true; - boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleI2NPMsgsSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); - } + // send buffers + m_IsSending = true; + boost::asio::async_write(m_Socket, bufs, boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleI2NPMsgsSent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2, msgs)); + } - void NTCP2Session::HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) - { - HandleNextFrameSent (ecode, bytes_transferred); - // msgs get destroyed here - } + void NTCP2Session::HandleI2NPMsgsSent(const boost::system::error_code &ecode, std::size_t bytes_transferred, + std::vector > msgs) { + HandleNextFrameSent(ecode, bytes_transferred); + // msgs get destroyed here + } - void NTCP2Session::EncryptAndSendNextBuffer (size_t payloadLen) - { - if (IsTerminated ()) - { - delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; - return; - } - // encrypt - uint8_t nonce[12]; - CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); - SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer); - // send - m_IsSending = true; - boost::asio::async_write (m_Socket, boost::asio::buffer (m_NextSendBuffer, payloadLen + 16 + 2), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleNextFrameSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void NTCP2Session::EncryptAndSendNextBuffer(size_t payloadLen) { + if (IsTerminated()) { + delete[] m_NextSendBuffer; + m_NextSendBuffer = nullptr; + return; + } + // encrypt + uint8_t nonce[12]; + CreateNonce(m_SendSequenceNumber, nonce); + m_SendSequenceNumber++; + i2p::crypto::AEADChaCha20Poly1305Encrypt({{m_NextSendBuffer + 2, payloadLen}}, m_SendKey, nonce, + m_NextSendBuffer + payloadLen + 2); + SetNextSentFrameLength(payloadLen + 16, m_NextSendBuffer); + // send + m_IsSending = true; + boost::asio::async_write(m_Socket, boost::asio::buffer(m_NextSendBuffer, payloadLen + 16 + 2), + boost::asio::transfer_all(), + std::bind(&NTCP2Session::HandleNextFrameSent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void NTCP2Session::HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - m_IsSending = false; - delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; + void NTCP2Session::HandleNextFrameSent(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + m_IsSending = false; + delete[] m_NextSendBuffer; + m_NextSendBuffer = nullptr; - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); - Terminate (); - } - else - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumSentBytes += bytes_transferred; - i2p::transport::transports.UpdateSentBytes (bytes_transferred); - LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); - if (m_LastActivityTimestamp > m_NextRouterInfoResendTime) - { - m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL + - rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; - SendRouterInfo (); - } - else - SendQueue (); - } - } + if (ecode) { + if (ecode != boost::asio::error::operation_aborted) + LogPrint(eLogWarning, "NTCP2: Couldn't send frame ", ecode.message()); + Terminate(); + } else { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + m_NumSentBytes += bytes_transferred; + i2p::transport::transports.UpdateSentBytes(bytes_transferred); + LogPrint(eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); + if (m_LastActivityTimestamp > m_NextRouterInfoResendTime) { + m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL + + rand() % NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; + SendRouterInfo(); + } else + SendQueue(); + } + } - void NTCP2Session::SendQueue () - { - if (!m_SendQueue.empty ()) - { - std::vector > msgs; - size_t s = 0; - while (!m_SendQueue.empty ()) - { - auto msg = m_SendQueue.front (); - size_t len = msg->GetNTCP2Length (); - if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header - { - msgs.push_back (msg); - s += (len + 3); - m_SendQueue.pop_front (); - } - else if (len + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) - { - LogPrint (eLogError, "NTCP2: I2NP message of size ", len, " can't be sent. Dropped"); - m_SendQueue.pop_front (); - } - else - break; - } - SendI2NPMsgs (msgs); - } - } + void NTCP2Session::SendQueue() { + if (!m_SendQueue.empty()) { + std::vector > msgs; + size_t s = 0; + while (!m_SendQueue.empty()) { + auto msg = m_SendQueue.front(); + size_t len = msg->GetNTCP2Length(); + if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header + { + msgs.push_back(msg); + s += (len + 3); + m_SendQueue.pop_front(); + } else if (len + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) { + LogPrint(eLogError, "NTCP2: I2NP message of size ", len, " can't be sent. Dropped"); + m_SendQueue.pop_front(); + } else + break; + } + SendI2NPMsgs(msgs); + } + } - size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) - { - if (len < 3) return 0; - len -= 3; - if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero - size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; - if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; - if (paddingSize > len) paddingSize = len; - if (paddingSize) - { - if (m_NextPaddingSize >= 16) - { - RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes)); - m_NextPaddingSize = 0; - } - paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize; - } - buf[0] = eNTCP2BlkPadding; // blk - htobe16buf (buf + 1, paddingSize); // size - memset (buf + 3, 0, paddingSize); - return paddingSize + 3; - } + size_t NTCP2Session::CreatePaddingBlock(size_t msgLen, uint8_t *buf, size_t len) { + if (len < 3) return 0; + len -= 3; + if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero + size_t paddingSize = (msgLen * NTCP2_MAX_PADDING_RATIO) / 100; + if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = + NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - + msgLen - 3; + if (paddingSize > len) paddingSize = len; + if (paddingSize) { + if (m_NextPaddingSize >= 16) { + RAND_bytes((uint8_t *) m_PaddingSizes, sizeof(m_PaddingSizes)); + m_NextPaddingSize = 0; + } + paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize; + } + buf[0] = eNTCP2BlkPadding; // blk + htobe16buf(buf + 1, paddingSize); // size + memset(buf + 3, 0, paddingSize); + return paddingSize + 3; + } - void NTCP2Session::SendRouterInfo () - { - if (!IsEstablished ()) return; - auto riLen = i2p::context.GetRouterInfo ().GetBufferLen (); - size_t payloadLen = riLen + 4; // 3 bytes block header + 1 byte RI flag - m_NextSendBuffer = new uint8_t[payloadLen + 16 + 2 + 64]; // up to 64 bytes padding - m_NextSendBuffer[2] = eNTCP2BlkRouterInfo; - htobe16buf (m_NextSendBuffer + 3, riLen + 1); // size - m_NextSendBuffer[5] = 0; // flag - memcpy (m_NextSendBuffer + 6, i2p::context.GetRouterInfo ().GetBuffer (), riLen); - // padding block - auto paddingSize = CreatePaddingBlock (payloadLen, m_NextSendBuffer + 2 + payloadLen, 64); - payloadLen += paddingSize; - // encrypt and send - EncryptAndSendNextBuffer (payloadLen); - } + void NTCP2Session::SendRouterInfo() { + if (!IsEstablished()) return; + auto riLen = i2p::context.GetRouterInfo().GetBufferLen(); + size_t payloadLen = riLen + 4; // 3 bytes block header + 1 byte RI flag + m_NextSendBuffer = new uint8_t[payloadLen + 16 + 2 + 64]; // up to 64 bytes padding + m_NextSendBuffer[2] = eNTCP2BlkRouterInfo; + htobe16buf(m_NextSendBuffer + 3, riLen + 1); // size + m_NextSendBuffer[5] = 0; // flag + memcpy(m_NextSendBuffer + 6, i2p::context.GetRouterInfo().GetBuffer(), riLen); + // padding block + auto paddingSize = CreatePaddingBlock(payloadLen, m_NextSendBuffer + 2 + payloadLen, 64); + payloadLen += paddingSize; + // encrypt and send + EncryptAndSendNextBuffer(payloadLen); + } - void NTCP2Session::SendTermination (NTCP2TerminationReason reason) - { - if (!m_SendKey || -#if OPENSSL_SIPHASH - !m_SendMDCtx -#else - !m_SendSipKey + void NTCP2Session::SendTermination(NTCP2TerminationReason reason) { + if (!m_SendKey || + #if OPENSSL_SIPHASH + !m_SendMDCtx + #else + !m_SendSipKey #endif - ) return; - m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block - // termination block - m_NextSendBuffer[2] = eNTCP2BlkTermination; - m_NextSendBuffer[3] = 0; m_NextSendBuffer[4] = 9; // 9 bytes block size - htobe64buf (m_NextSendBuffer + 5, m_ReceiveSequenceNumber); - m_NextSendBuffer[13] = (uint8_t)reason; - // padding block - auto paddingSize = CreatePaddingBlock (12, m_NextSendBuffer + 14, 19); - // encrypt and send - EncryptAndSendNextBuffer (paddingSize + 12); - } + ) + return; + m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block + // termination block + m_NextSendBuffer[2] = eNTCP2BlkTermination; + m_NextSendBuffer[3] = 0; + m_NextSendBuffer[4] = 9; // 9 bytes block size + htobe64buf(m_NextSendBuffer + 5, m_ReceiveSequenceNumber); + m_NextSendBuffer[13] = (uint8_t) reason; + // padding block + auto paddingSize = CreatePaddingBlock(12, m_NextSendBuffer + 14, 19); + // encrypt and send + EncryptAndSendNextBuffer(paddingSize + 12); + } - void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason) - { - SendTermination (reason); - m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go - } + void NTCP2Session::SendTerminationAndTerminate(NTCP2TerminationReason reason) { + SendTermination(reason); + m_Server.GetService().post( + std::bind(&NTCP2Session::Terminate, shared_from_this())); // let termination message go + } - void NTCP2Session::SendI2NPMessages (const std::vector >& msgs) - { - m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs)); - } + void NTCP2Session::SendI2NPMessages(const std::vector > &msgs) { + m_Server.GetService().post(std::bind(&NTCP2Session::PostI2NPMessages, shared_from_this(), msgs)); + } - void NTCP2Session::PostI2NPMessages (std::vector > msgs) - { - if (m_IsTerminated) return; - for (auto it: msgs) - m_SendQueue.push_back (it); - if (!m_IsSending) - SendQueue (); - else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) - { - LogPrint (eLogWarning, "NTCP2: Outgoing messages queue size to ", - GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); - Terminate (); - } - } + void NTCP2Session::PostI2NPMessages(std::vector > msgs) { + if (m_IsTerminated) return; + for (auto it: msgs) + m_SendQueue.push_back(it); + if (!m_IsSending) + SendQueue(); + else if (m_SendQueue.size() > NTCP2_MAX_OUTGOING_QUEUE_SIZE) { + LogPrint(eLogWarning, "NTCP2: Outgoing messages queue size to ", + GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); + Terminate(); + } + } - void NTCP2Session::SendLocalRouterInfo (bool update) - { - if (update || !IsOutgoing ()) // we send it in SessionConfirmed for ougoing session - m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); - } + void NTCP2Session::SendLocalRouterInfo(bool update) { + if (update || !IsOutgoing()) // we send it in SessionConfirmed for ougoing session + m_Server.GetService().post(std::bind(&NTCP2Session::SendRouterInfo, shared_from_this())); + } - NTCP2Server::NTCP2Server (): - RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), - m_ProxyType(eNoProxy), m_Resolver(GetService ()) - { - } + NTCP2Server::NTCP2Server() : + RunnableServiceWithWork("NTCP2"), m_TerminationTimer(GetService()), + m_ProxyType(eNoProxy), m_Resolver(GetService()) { + } - NTCP2Server::~NTCP2Server () - { - Stop (); - } + NTCP2Server::~NTCP2Server() { + Stop(); + } - void NTCP2Server::Start () - { - if (!IsRunning ()) - { - StartIOService (); - if(UsingProxy()) - { - LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers"); - // TODO: resolve proxy until it is resolved - boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); - boost::system::error_code e; - auto itr = m_Resolver.resolve(q, e); - if(e) - LogPrint(eLogError, "NTCP2: Failed to resolve proxy ", e.message()); - else - { - m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr)); - if (m_ProxyEndpoint) - LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); - } - } - else - LogPrint(eLogInfo, "NTCP2: Proxy is not used"); - // start acceptors - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) - { - if (!address) continue; - if (address->IsPublishedNTCP2 () && address->port) - { - if (address->IsV4()) - { - try - { - auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port): - boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port); - m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep)); - } - catch ( std::exception & ex ) - { - LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what()); - ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ()); - continue; - } + void NTCP2Server::Start() { + if (!IsRunning()) { + StartIOService(); + if (UsingProxy()) { + LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers"); + // TODO: resolve proxy until it is resolved + boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); + boost::system::error_code e; + auto itr = m_Resolver.resolve(q, e); + if (e) + LogPrint(eLogError, "NTCP2: Failed to resolve proxy ", e.message()); + else { + m_ProxyEndpoint.reset(new boost::asio::ip::tcp::endpoint(*itr)); + if (m_ProxyEndpoint) + LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); + } + } else + LogPrint(eLogInfo, "NTCP2: Proxy is not used"); + // start acceptors + auto &addresses = context.GetRouterInfo().GetAddresses(); + for (const auto &address: addresses) { + if (!address) continue; + if (address->IsPublishedNTCP2() && address->port) { + if (address->IsV4()) { + try { + auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint(m_Address4->address(), + address->port) : + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port); + m_NTCP2Acceptor.reset(new boost::asio::ip::tcp::acceptor(GetService(), ep)); + } + catch (std::exception &ex) { + LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what()); + ThrowFatal("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", + ex.what()); + continue; + } - LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port); - auto conn = std::make_shared(*this); - m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); - } - else if (address->IsV6() && (context.SupportsV6 () || context.SupportsMesh ())) - { - m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); - try - { - m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); - m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); - m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); + LogPrint(eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port); + auto conn = std::make_shared(*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket(), + std::bind(&NTCP2Server::HandleAccept, this, conn, + std::placeholders::_1)); + } else if (address->IsV6() && (context.SupportsV6() || context.SupportsMesh())) { + m_NTCP2V6Acceptor.reset(new boost::asio::ip::tcp::acceptor(GetService())); + try { + m_NTCP2V6Acceptor->open(boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option(boost::asio::ip::v6_only(true)); + m_NTCP2V6Acceptor->set_option(boost::asio::socket_base::reuse_address(true)); #if defined(__linux__) && !defined(_NETINET_IN_H) - if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address - { - // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others + if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address + { + // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others #if (BOOST_VERSION >= 105500) - typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; #else - typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; #endif - m_NTCP2V6Acceptor->set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); - } + m_NTCP2V6Acceptor->set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); + } #endif - auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port); - if (m_Address6 && !context.SupportsMesh ()) - ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port); - else if (m_YggdrasilAddress && !context.SupportsV6 ()) - ep = boost::asio::ip::tcp::endpoint (m_YggdrasilAddress->address(), address->port); - m_NTCP2V6Acceptor->bind (ep); - m_NTCP2V6Acceptor->listen (); + auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port); + if (m_Address6 && !context.SupportsMesh()) + ep = boost::asio::ip::tcp::endpoint(m_Address6->address(), address->port); + else if (m_YggdrasilAddress && !context.SupportsV6()) + ep = boost::asio::ip::tcp::endpoint(m_YggdrasilAddress->address(), address->port); + m_NTCP2V6Acceptor->bind(ep); + m_NTCP2V6Acceptor->listen(); - LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port); - auto conn = std::make_shared (*this); - m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); - } - catch ( std::exception & ex ) - { - LogPrint(eLogError, "NTCP2: Failed to bind to v6 port ", address->port, ": ", ex.what()); - ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ()); - continue; - } - } - } - } - ScheduleTermination (); - } - } + LogPrint(eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port); + auto conn = std::make_shared(*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket(), + std::bind(&NTCP2Server::HandleAcceptV6, this, conn, + std::placeholders::_1)); + } + catch (std::exception &ex) { + LogPrint(eLogError, "NTCP2: Failed to bind to v6 port ", address->port, ": ", + ex.what()); + ThrowFatal("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", + ex.what()); + continue; + } + } + } + } + ScheduleTermination(); + } + } - void NTCP2Server::Stop () - { - { - // we have to copy it because Terminate changes m_NTCP2Sessions - auto ntcpSessions = m_NTCP2Sessions; - for (auto& it: ntcpSessions) - it.second->Terminate (); - for (auto& it: m_PendingIncomingSessions) - it->Terminate (); - } - m_NTCP2Sessions.clear (); + void NTCP2Server::Stop() { + { + // we have to copy it because Terminate changes m_NTCP2Sessions + auto ntcpSessions = m_NTCP2Sessions; + for (auto &it: ntcpSessions) + it.second->Terminate(); + for (auto &it: m_PendingIncomingSessions) + it->Terminate(); + } + m_NTCP2Sessions.clear(); - if (IsRunning ()) - { - m_TerminationTimer.cancel (); - m_ProxyEndpoint = nullptr; - } - StopIOService (); - } + if (IsRunning()) { + m_TerminationTimer.cancel(); + m_ProxyEndpoint = nullptr; + } + StopIOService(); + } - bool NTCP2Server::AddNTCP2Session (std::shared_ptr session, bool incoming) - { - if (!session) return false; - if (incoming) - m_PendingIncomingSessions.remove (session); - if (!session->GetRemoteIdentity ()) return false; - auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); - auto it = m_NTCP2Sessions.find (ident); - if (it != m_NTCP2Sessions.end ()) - { - LogPrint (eLogWarning, "NTCP2: Session to ", ident.ToBase64 (), " already exists"); - if (incoming) - // replace by new session - it->second->Terminate (); - else - return false; - } - m_NTCP2Sessions.insert (std::make_pair (ident, session)); - return true; - } + bool NTCP2Server::AddNTCP2Session(std::shared_ptr session, bool incoming) { + if (!session) return false; + if (incoming) + m_PendingIncomingSessions.remove(session); + if (!session->GetRemoteIdentity()) return false; + auto &ident = session->GetRemoteIdentity()->GetIdentHash(); + auto it = m_NTCP2Sessions.find(ident); + if (it != m_NTCP2Sessions.end()) { + LogPrint(eLogWarning, "NTCP2: Session to ", ident.ToBase64(), " already exists"); + if (incoming) + // replace by new session + it->second->Terminate(); + else + return false; + } + m_NTCP2Sessions.insert(std::make_pair(ident, session)); + return true; + } - void NTCP2Server::RemoveNTCP2Session (std::shared_ptr session) - { - if (session && session->GetRemoteIdentity ()) - m_NTCP2Sessions.erase (session->GetRemoteIdentity ()->GetIdentHash ()); - } + void NTCP2Server::RemoveNTCP2Session(std::shared_ptr session) { + if (session && session->GetRemoteIdentity()) + m_NTCP2Sessions.erase(session->GetRemoteIdentity()->GetIdentHash()); + } - std::shared_ptr NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident) - { - auto it = m_NTCP2Sessions.find (ident); - if (it != m_NTCP2Sessions.end ()) - return it->second; - return nullptr; - } + std::shared_ptr NTCP2Server::FindNTCP2Session(const i2p::data::IdentHash &ident) { + auto it = m_NTCP2Sessions.find(ident); + if (it != m_NTCP2Sessions.end()) + return it->second; + return nullptr; + } - void NTCP2Server::Connect(std::shared_ptr conn) - { - if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ()) - { - LogPrint (eLogError, "NTCP2: Can't connect to unspecified address"); - return; - } - LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ()); - GetService ().post([this, conn]() - { - if (this->AddNTCP2Session (conn)) - { - auto timer = std::make_shared(GetService ()); - auto timeout = NTCP2_CONNECT_TIMEOUT * 5; - conn->SetTerminationTimeout(timeout * 2); - timer->expires_from_now (boost::posix_time::seconds(timeout)); - timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); - conn->Terminate (); - } - }); - // bind to local address - std::shared_ptr localAddress; - if (conn->GetRemoteEndpoint ().address ().is_v6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (conn->GetRemoteEndpoint ().address ())) - localAddress = m_YggdrasilAddress; - else - localAddress = m_Address6; - conn->GetSocket ().open (boost::asio::ip::tcp::v6 ()); - } - else - { - localAddress = m_Address4; - conn->GetSocket ().open (boost::asio::ip::tcp::v4 ()); - } - if (localAddress) - { - boost::system::error_code ec; - conn->GetSocket ().bind (*localAddress, ec); - if (ec) - LogPrint (eLogError, "NTCP2: Can't bind to ", localAddress->address ().to_string (), ": ", ec.message ()); - } - conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); - } - else - conn->Terminate (); - }); - } + void NTCP2Server::Connect(std::shared_ptr conn) { + if (!conn || conn->GetRemoteEndpoint().address().is_unspecified()) { + LogPrint(eLogError, "NTCP2: Can't connect to unspecified address"); + return; + } + LogPrint(eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint()); + GetService().post([this, conn]() { + if (this->AddNTCP2Session(conn)) { + auto timer = std::make_shared(GetService()); + auto timeout = NTCP2_CONNECT_TIMEOUT * 5; + conn->SetTerminationTimeout(timeout * 2); + timer->expires_from_now(boost::posix_time::seconds(timeout)); + timer->async_wait([conn, timeout](const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); + conn->Terminate(); + } + }); + // bind to local address + std::shared_ptr localAddress; + if (conn->GetRemoteEndpoint().address().is_v6()) { + if (i2p::util::net::IsYggdrasilAddress(conn->GetRemoteEndpoint().address())) + localAddress = m_YggdrasilAddress; + else + localAddress = m_Address6; + conn->GetSocket().open(boost::asio::ip::tcp::v6()); + } else { + localAddress = m_Address4; + conn->GetSocket().open(boost::asio::ip::tcp::v4()); + } + if (localAddress) { + boost::system::error_code ec; + conn->GetSocket().bind(*localAddress, ec); + if (ec) + LogPrint(eLogError, "NTCP2: Can't bind to ", localAddress->address().to_string(), ": ", + ec.message()); + } + conn->GetSocket().async_connect(conn->GetRemoteEndpoint(), + std::bind(&NTCP2Server::HandleConnect, this, std::placeholders::_1, + conn, timer)); + } else + conn->Terminate(); + }); + } - void NTCP2Server::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer) - { - timer->cancel (); - if (ecode) - { - LogPrint (eLogInfo, "NTCP2: Connect error ", ecode.message ()); - conn->Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ()); - conn->ClientLogin (); - } - } + void NTCP2Server::HandleConnect(const boost::system::error_code &ecode, std::shared_ptr conn, + std::shared_ptr timer) { + timer->cancel(); + if (ecode) { + LogPrint(eLogInfo, "NTCP2: Connect error ", ecode.message()); + conn->Terminate(); + } else { + LogPrint(eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint()); + conn->ClientLogin(); + } + } - void NTCP2Server::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) - { - if (!error) - { - boost::system::error_code ec; - auto ep = conn->GetSocket ().remote_endpoint(ec); - if (!ec) - { - LogPrint (eLogDebug, "NTCP2: Connected from ", ep); - if (conn) - { - conn->SetRemoteEndpoint (ep); - conn->ServerLogin (); - m_PendingIncomingSessions.push_back (conn); - conn = nullptr; - } - } - else - LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); - } - else - LogPrint (eLogError, "NTCP2: Accept error ", error.message ()); + void NTCP2Server::HandleAccept(std::shared_ptr conn, const boost::system::error_code &error) { + if (!error) { + boost::system::error_code ec; + auto ep = conn->GetSocket().remote_endpoint(ec); + if (!ec) { + LogPrint(eLogDebug, "NTCP2: Connected from ", ep); + if (conn) { + conn->SetRemoteEndpoint(ep); + conn->ServerLogin(); + m_PendingIncomingSessions.push_back(conn); + conn = nullptr; + } + } else + LogPrint(eLogError, "NTCP2: Connected from error ", ec.message()); + } else + LogPrint(eLogError, "NTCP2: Accept error ", error.message()); - if (error != boost::asio::error::operation_aborted) - { - if (!conn) // connection is used, create new one - conn = std::make_shared (*this); - else // reuse failed - conn->Close (); - m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, - conn, std::placeholders::_1)); - } - } + if (error != boost::asio::error::operation_aborted) { + if (!conn) // connection is used, create new one + conn = std::make_shared(*this); + else // reuse failed + conn->Close(); + m_NTCP2Acceptor->async_accept(conn->GetSocket(), std::bind(&NTCP2Server::HandleAccept, this, + conn, std::placeholders::_1)); + } + } - void NTCP2Server::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) - { - if (!error) - { - boost::system::error_code ec; - auto ep = conn->GetSocket ().remote_endpoint(ec); - if (!ec) - { - LogPrint (eLogDebug, "NTCP2: Connected from ", ep); - if (conn) - { - conn->SetRemoteEndpoint (ep); - conn->ServerLogin (); - m_PendingIncomingSessions.push_back (conn); - } - } - else - LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); - } + void NTCP2Server::HandleAcceptV6(std::shared_ptr conn, const boost::system::error_code &error) { + if (!error) { + boost::system::error_code ec; + auto ep = conn->GetSocket().remote_endpoint(ec); + if (!ec) { + LogPrint(eLogDebug, "NTCP2: Connected from ", ep); + if (conn) { + conn->SetRemoteEndpoint(ep); + conn->ServerLogin(); + m_PendingIncomingSessions.push_back(conn); + } + } else + LogPrint(eLogError, "NTCP2: Connected from error ", ec.message()); + } - if (error != boost::asio::error::operation_aborted) - { - conn = std::make_shared (*this); - m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, - conn, std::placeholders::_1)); - } - } + if (error != boost::asio::error::operation_aborted) { + conn = std::make_shared(*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket(), std::bind(&NTCP2Server::HandleAcceptV6, this, + conn, std::placeholders::_1)); + } + } - void NTCP2Server::ScheduleTermination () - { - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP2_TERMINATION_CHECK_TIMEOUT)); - m_TerminationTimer.async_wait (std::bind (&NTCP2Server::HandleTerminationTimer, - this, std::placeholders::_1)); - } + void NTCP2Server::ScheduleTermination() { + m_TerminationTimer.expires_from_now(boost::posix_time::seconds(NTCP2_TERMINATION_CHECK_TIMEOUT)); + m_TerminationTimer.async_wait(std::bind(&NTCP2Server::HandleTerminationTimer, + this, std::placeholders::_1)); + } - void NTCP2Server::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - // established - for (auto& it: m_NTCP2Sessions) - if (it.second->IsTerminationTimeoutExpired (ts)) - { - auto session = it.second; - LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds"); - session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way - } - else - it.second->DeleteNextReceiveBuffer (ts); - // pending - for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) - { - if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts)) - { - (*it)->Terminate (); - it = m_PendingIncomingSessions.erase (it); // established of expired - } - else if ((*it)->IsTerminated ()) - it = m_PendingIncomingSessions.erase (it); // already terminated - else - it++; - } + void NTCP2Server::HandleTerminationTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ts = i2p::util::GetSecondsSinceEpoch(); + // established + for (auto &it: m_NTCP2Sessions) + if (it.second->IsTerminationTimeoutExpired(ts)) { + auto session = it.second; + LogPrint(eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout(), " seconds"); + session->TerminateByTimeout(); // it doesn't change m_NTCP2Session right a way + } else + it.second->DeleteNextReceiveBuffer(ts); + // pending + for (auto it = m_PendingIncomingSessions.begin(); it != m_PendingIncomingSessions.end();) { + if ((*it)->IsEstablished() || (*it)->IsTerminationTimeoutExpired(ts)) { + (*it)->Terminate(); + it = m_PendingIncomingSessions.erase(it); // established of expired + } else if ((*it)->IsTerminated()) + it = m_PendingIncomingSessions.erase(it); // already terminated + else + it++; + } - ScheduleTermination (); - } - } + ScheduleTermination(); + } + } - void NTCP2Server::ConnectWithProxy (std::shared_ptr conn) - { - if(!m_ProxyEndpoint) return; - if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ()) - { - LogPrint (eLogError, "NTCP2: Can't connect to unspecified address"); - return; - } - GetService().post([this, conn]() - { - if (this->AddNTCP2Session (conn)) - { - auto timer = std::make_shared(GetService()); - auto timeout = NTCP2_CONNECT_TIMEOUT * 5; - conn->SetTerminationTimeout(timeout * 2); - timer->expires_from_now (boost::posix_time::seconds(timeout)); - timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); - conn->Terminate (); - } - }); - conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer)); - } - }); - } + void NTCP2Server::ConnectWithProxy(std::shared_ptr conn) { + if (!m_ProxyEndpoint) return; + if (!conn || conn->GetRemoteEndpoint().address().is_unspecified()) { + LogPrint(eLogError, "NTCP2: Can't connect to unspecified address"); + return; + } + GetService().post([this, conn]() { + if (this->AddNTCP2Session(conn)) { + auto timer = std::make_shared(GetService()); + auto timeout = NTCP2_CONNECT_TIMEOUT * 5; + conn->SetTerminationTimeout(timeout * 2); + timer->expires_from_now(boost::posix_time::seconds(timeout)); + timer->async_wait([conn, timeout](const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); + conn->Terminate(); + } + }); + conn->GetSocket().async_connect(*m_ProxyEndpoint, std::bind(&NTCP2Server::HandleProxyConnect, this, + std::placeholders::_1, conn, timer)); + } + }); + } - void NTCP2Server::UseProxy(ProxyType proxytype, const std::string& addr, uint16_t port, - const std::string& user, const std::string& pass) - { - m_ProxyType = proxytype; - m_ProxyAddress = addr; - m_ProxyPort = port; - if (m_ProxyType == eHTTPProxy ) - m_ProxyAuthorization = i2p::http::CreateBasicAuthorizationString (user, pass); - } + void NTCP2Server::UseProxy(ProxyType proxytype, const std::string &addr, uint16_t port, + const std::string &user, const std::string &pass) { + m_ProxyType = proxytype; + m_ProxyAddress = addr; + m_ProxyPort = port; + if (m_ProxyType == eHTTPProxy) + m_ProxyAuthorization = i2p::http::CreateBasicAuthorizationString(user, pass); + } - void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer) - { - if (ecode) - { - LogPrint(eLogWarning, "NTCP2: Failed to connect to proxy ", ecode.message()); - timer->cancel(); - conn->Terminate(); - return; - } - switch (m_ProxyType) - { - case eSocksProxy: - { - // TODO: support username/password auth etc - static const uint8_t buff[3] = {0x05, 0x01, 0x00}; - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), - [] (const boost::system::error_code & ec, std::size_t transferred) - { - (void) transferred; - if(ec) - { - LogPrint(eLogWarning, "NTCP2: SOCKS5 write error ", ec.message()); - } - }); - auto readbuff = std::make_shared >(2); - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), - [this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) - { - LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message()); - timer->cancel(); - conn->Terminate(); - return; - } - else if(transferred == 2) - { - if((*readbuff)[1] == 0x00) - { - AfterSocksHandshake(conn, timer); - return; - } - else if ((*readbuff)[1] == 0xff) - { - LogPrint(eLogError, "NTCP2: SOCKS5 proxy rejected authentication"); - timer->cancel(); - conn->Terminate(); - return; - } - LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]); - } - LogPrint(eLogError, "NTCP2: SOCKS5 server gave invalid response"); - timer->cancel(); - conn->Terminate(); - }); - break; - } - case eHTTPProxy: - { - auto& ep = conn->GetRemoteEndpoint (); - i2p::http::HTTPReq req; - req.method = "CONNECT"; - req.version ="HTTP/1.1"; - if(ep.address ().is_v6 ()) - req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ()); - else - req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ()); - if (!m_ProxyAuthorization.empty ()) - req.AddHeader("Proxy-Authorization", m_ProxyAuthorization); + void NTCP2Server::HandleProxyConnect(const boost::system::error_code &ecode, std::shared_ptr conn, + std::shared_ptr timer) { + if (ecode) { + LogPrint(eLogWarning, "NTCP2: Failed to connect to proxy ", ecode.message()); + timer->cancel(); + conn->Terminate(); + return; + } + switch (m_ProxyType) { + case eSocksProxy: { + // TODO: support username/password auth etc + static const uint8_t buff[3] = {0x05, 0x01, 0x00}; + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), + boost::asio::transfer_all(), + [](const boost::system::error_code &ec, std::size_t transferred) { + (void) transferred; + if (ec) { + LogPrint(eLogWarning, "NTCP2: SOCKS5 write error ", ec.message()); + } + }); + auto readbuff = std::make_shared >(2); + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data(), 2), + [this, readbuff, timer, conn](const boost::system::error_code &ec, + std::size_t transferred) { + if (ec) { + LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message()); + timer->cancel(); + conn->Terminate(); + return; + } else if (transferred == 2) { + if ((*readbuff)[1] == 0x00) { + AfterSocksHandshake(conn, timer); + return; + } else if ((*readbuff)[1] == 0xff) { + LogPrint(eLogError, + "NTCP2: SOCKS5 proxy rejected authentication"); + timer->cancel(); + conn->Terminate(); + return; + } + LogPrint(eLogError, "NTCP2:", (int) (*readbuff)[1]); + } + LogPrint(eLogError, "NTCP2: SOCKS5 server gave invalid response"); + timer->cancel(); + conn->Terminate(); + }); + break; + } + case eHTTPProxy: { + auto &ep = conn->GetRemoteEndpoint(); + i2p::http::HTTPReq req; + req.method = "CONNECT"; + req.version = "HTTP/1.1"; + if (ep.address().is_v6()) + req.uri = "[" + ep.address().to_string() + "]:" + std::to_string(ep.port()); + else + req.uri = ep.address().to_string() + ":" + std::to_string(ep.port()); + if (!m_ProxyAuthorization.empty()) + req.AddHeader("Proxy-Authorization", m_ProxyAuthorization); - boost::asio::streambuf writebuff; - std::ostream out(&writebuff); - out << req.to_string(); + boost::asio::streambuf writebuff; + std::ostream out(&writebuff); + out << req.to_string(); - boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), - [](const boost::system::error_code & ec, std::size_t transferred) - { - (void) transferred; - if(ec) - LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message()); - }); + boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), + [](const boost::system::error_code &ec, std::size_t transferred) { + (void) transferred; + if (ec) + LogPrint(eLogError, "NTCP2: HTTP proxy write error ", + ec.message()); + }); - boost::asio::streambuf * readbuff = new boost::asio::streambuf; - boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", - [readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) - { - LogPrint(eLogError, "NTCP2: HTTP proxy read error ", ec.message()); - timer->cancel(); - conn->Terminate(); - } - else - { - readbuff->commit(transferred); - i2p::http::HTTPRes res; - if(res.parse(boost::asio::buffer_cast(readbuff->data()), readbuff->size()) > 0) - { - if(res.code == 200) - { - timer->cancel(); - conn->ClientLogin(); - delete readbuff; - return; - } - else - LogPrint(eLogError, "NTCP2: HTTP proxy rejected request ", res.code); - } - else - LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response"); - timer->cancel(); - conn->Terminate(); - delete readbuff; - } - }); - break; - } - default: - LogPrint(eLogError, "NTCP2: Unknown proxy type, invalid state"); - } - } + boost::asio::streambuf *readbuff = new boost::asio::streambuf; + boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", + [readbuff, timer, conn](const boost::system::error_code &ec, + std::size_t transferred) { + if (ec) { + LogPrint(eLogError, "NTCP2: HTTP proxy read error ", + ec.message()); + timer->cancel(); + conn->Terminate(); + } else { + readbuff->commit(transferred); + i2p::http::HTTPRes res; + if (res.parse(boost::asio::buffer_cast( + readbuff->data()), readbuff->size()) > 0) { + if (res.code == 200) { + timer->cancel(); + conn->ClientLogin(); + delete readbuff; + return; + } else + LogPrint(eLogError, + "NTCP2: HTTP proxy rejected request ", + res.code); + } else + LogPrint(eLogError, + "NTCP2: HTTP proxy gave malformed response"); + timer->cancel(); + conn->Terminate(); + delete readbuff; + } + }); + break; + } + default: + LogPrint(eLogError, "NTCP2: Unknown proxy type, invalid state"); + } + } - void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer) - { - // build request - size_t sz = 6; // header + port - auto buff = std::make_shared >(256); - auto readbuff = std::make_shared >(256); - (*buff)[0] = 0x05; - (*buff)[1] = 0x01; - (*buff)[2] = 0x00; + void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, + std::shared_ptr timer) { + // build request + size_t sz = 6; // header + port + auto buff = std::make_shared >(256); + auto readbuff = std::make_shared >(256); + (*buff)[0] = 0x05; + (*buff)[1] = 0x01; + (*buff)[2] = 0x00; - auto& ep = conn->GetRemoteEndpoint (); - if(ep.address ().is_v4 ()) - { - (*buff)[3] = 0x01; - auto addrbytes = ep.address ().to_v4().to_bytes(); - sz += 4; - memcpy(buff->data () + 4, addrbytes.data(), 4); - } - else if (ep.address ().is_v6 ()) - { - (*buff)[3] = 0x04; - auto addrbytes = ep.address ().to_v6().to_bytes(); - sz += 16; - memcpy(buff->data () + 4, addrbytes.data(), 16); - } - else - { - // We mustn't really fall here because all connections are made to IP addresses - LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy"); - return; - } - htobe16buf(buff->data () + sz - 2, ep.port ()); - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), - [buff](const boost::system::error_code & ec, std::size_t written) - { - if(ec) - { - LogPrint(eLogError, "NTCP2: Failed to write handshake to socks proxy ", ec.message()); - return; - } - }); + auto &ep = conn->GetRemoteEndpoint(); + if (ep.address().is_v4()) { + (*buff)[3] = 0x01; + auto addrbytes = ep.address().to_v4().to_bytes(); + sz += 4; + memcpy(buff->data() + 4, addrbytes.data(), 4); + } else if (ep.address().is_v6()) { + (*buff)[3] = 0x04; + auto addrbytes = ep.address().to_v6().to_bytes(); + sz += 16; + memcpy(buff->data() + 4, addrbytes.data(), 16); + } else { + // We mustn't really fall here because all connections are made to IP addresses + LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy"); + return; + } + htobe16buf(buff->data() + sz - 2, ep.port()); + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data(), sz), + boost::asio::transfer_all(), + [buff](const boost::system::error_code &ec, std::size_t written) { + if (ec) { + LogPrint(eLogError, "NTCP2: Failed to write handshake to socks proxy ", + ec.message()); + return; + } + }); - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10), - [timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) - { - if(e) - { - LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message()); - } - else if(transferred == sz) - { - if((*readbuff)[1] == 0x00) - { - timer->cancel(); - conn->ClientLogin(); - return; - } - } - timer->cancel(); - conn->Terminate(); - }); - } + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data(), 10), + [timer, conn, sz, readbuff](const boost::system::error_code &e, + std::size_t transferred) { + if (e) { + LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message()); + } else if (transferred == sz) { + if ((*readbuff)[1] == 0x00) { + timer->cancel(); + conn->ClientLogin(); + return; + } + } + timer->cancel(); + conn->Terminate(); + }); + } - void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) - { - auto addr = std::make_shared(boost::asio::ip::tcp::endpoint(localAddress, 0)); - if (localAddress.is_v6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (localAddress)) - m_YggdrasilAddress = addr; - else - m_Address6 = addr; - } - else - m_Address4 = addr; - } -} + void NTCP2Server::SetLocalAddress(const boost::asio::ip::address &localAddress) { + auto addr = std::make_shared( + boost::asio::ip::tcp::endpoint(localAddress, 0)); + if (localAddress.is_v6()) { + if (i2p::util::net::IsYggdrasilAddress(localAddress)) + m_YggdrasilAddress = addr; + else + m_Address6 = addr; + } else + m_Address4 = addr; + } + } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 754f5a6d..f86d58aa 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -22,276 +22,339 @@ #include "RouterInfo.h" #include "TransportSession.h" -namespace i2p -{ -namespace transport -{ +namespace i2p { + namespace transport { - const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; - const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; - const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; - const int NTCP2_MAX_PADDING_RATIO = 6; // in % + const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; + const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; + const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; + const int NTCP2_MAX_PADDING_RATIO = 6; // in % - const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds - const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds - const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes - const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds - 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_THRESHOLD = 25*60; // 25 minuntes + const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds + const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds + const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes + const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds + 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_THRESHOLD = 25 * 60; // 25 minuntes - 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_CLOCK_SKEW = 60; // in seconds + const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up - enum NTCP2BlockType - { - eNTCP2BlkDateTime = 0, - eNTCP2BlkOptions, // 1 - eNTCP2BlkRouterInfo, // 2 - eNTCP2BlkI2NPMessage, // 3 - eNTCP2BlkTermination, // 4 - eNTCP2BlkPadding = 254 - }; + enum NTCP2BlockType { + eNTCP2BlkDateTime = 0, + eNTCP2BlkOptions, // 1 + eNTCP2BlkRouterInfo, // 2 + eNTCP2BlkI2NPMessage, // 3 + eNTCP2BlkTermination, // 4 + eNTCP2BlkPadding = 254 + }; - enum NTCP2TerminationReason - { - eNTCP2NormalClose = 0, - eNTCP2TerminationReceived, // 1 - eNTCP2IdleTimeout, // 2 - eNTCP2RouterShutdown, // 3 - eNTCP2DataPhaseAEADFailure, // 4 - eNTCP2IncompatibleOptions, // 5 - eNTCP2IncompatibleSignatureType, // 6 - eNTCP2ClockSkew, // 7 - eNTCP2PaddingViolation, // 8 - eNTCP2AEADFramingError, // 9 - eNTCP2PayloadFormatError, // 10 - eNTCP2Message1Error, // 11 - eNTCP2Message2Error, // 12 - eNTCP2Message3Error, // 13 - eNTCP2IntraFrameReadTimeout, // 14 - eNTCP2RouterInfoSignatureVerificationFail, // 15 - eNTCP2IncorrectSParameter, // 16 - eNTCP2Banned, // 17 - }; + enum NTCP2TerminationReason { + eNTCP2NormalClose = 0, + eNTCP2TerminationReceived, // 1 + eNTCP2IdleTimeout, // 2 + eNTCP2RouterShutdown, // 3 + eNTCP2DataPhaseAEADFailure, // 4 + eNTCP2IncompatibleOptions, // 5 + eNTCP2IncompatibleSignatureType, // 6 + eNTCP2ClockSkew, // 7 + eNTCP2PaddingViolation, // 8 + eNTCP2AEADFramingError, // 9 + eNTCP2PayloadFormatError, // 10 + eNTCP2Message1Error, // 11 + eNTCP2Message2Error, // 12 + eNTCP2Message3Error, // 13 + eNTCP2IntraFrameReadTimeout, // 14 + eNTCP2RouterInfoSignatureVerificationFail, // 15 + eNTCP2IncorrectSParameter, // 16 + eNTCP2Banned, // 17 + }; - // RouterInfo flags - const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; + // RouterInfo flags + const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; - struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState - { - NTCP2Establisher (); - ~NTCP2Establisher (); + struct NTCP2Establisher : private i2p::crypto::NoiseSymmetricState { + NTCP2Establisher(); - const uint8_t * GetPub () const { return m_EphemeralKeys->GetPublicKey (); }; - const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob - uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set + ~NTCP2Establisher(); - const uint8_t * GetK () const { return m_CK + 32; }; - const uint8_t * GetCK () const { return m_CK; }; - const uint8_t * GetH () const { return m_H; }; + const uint8_t *GetPub() const { return m_EphemeralKeys->GetPublicKey(); }; - void KDF1Alice (); - void KDF1Bob (); - void KDF2Alice (); - void KDF2Bob (); - void KDF3Alice (); // for SessionConfirmed part 2 - void KDF3Bob (); + const uint8_t *GetRemotePub() const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob + uint8_t *GetRemotePub() { return m_RemoteEphemeralPublicKey; }; // to set - void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH - void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate - void CreateEphemeralKey (); + const uint8_t *GetK() const { return m_CK + 32; }; - void CreateSessionRequestMessage (); - void CreateSessionCreatedMessage (); - void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); - void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); + const uint8_t *GetCK() const { return m_CK; }; - bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew); - bool ProcessSessionCreatedMessage (uint16_t& paddingLen); - bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); - bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf); + const uint8_t *GetH() const { return m_H; }; - std::shared_ptr m_EphemeralKeys; - uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 - uint8_t m_RemoteStaticKey[32], m_IV[16]; - i2p::data::IdentHash m_RemoteIdentHash; - uint16_t m3p2Len; + void KDF1Alice(); - uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], - m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer; - size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; + void KDF1Bob(); - }; + void KDF2Alice(); - class NTCP2Server; - class NTCP2Session: public TransportSession, public std::enable_shared_from_this - { - public: + void KDF2Bob(); - NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr, - std::shared_ptr addr = nullptr); - ~NTCP2Session (); - void Terminate (); - void TerminateByTimeout (); - void Done (); - void Close () { m_Socket.close (); }; // for accept - void DeleteNextReceiveBuffer (uint64_t ts); + void KDF3Alice(); // for SessionConfirmed part 2 + void KDF3Bob(); - 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; }; + void KeyDerivationFunction1(const uint8_t *pub, i2p::crypto::X25519Keys &priv, const uint8_t *rs, + const uint8_t *epub); // for SessionRequest, (pub, priv) for DH + void KeyDerivationFunction2(const uint8_t *sessionRequest, size_t sessionRequestLen, + const uint8_t *epub); // for SessionCreate + void CreateEphemeralKey(); - bool IsEstablished () const { return m_IsEstablished; }; - bool IsTerminated () const { return m_IsTerminated; }; + void CreateSessionRequestMessage(); - void ClientLogin (); // Alice - void ServerLogin (); // Bob + void CreateSessionCreatedMessage(); - void SendLocalRouterInfo (bool update); // after handshake or by update - void SendI2NPMessages (const std::vector >& msgs); + void CreateSessionConfirmedMessagePart1(const uint8_t *nonce); - private: + void CreateSessionConfirmedMessagePart2(const uint8_t *nonce); - void Established (); + bool ProcessSessionRequestMessage(uint16_t &paddingLen, bool &clockSkew); - 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); + bool ProcessSessionCreatedMessage(uint16_t &paddingLen); - // establish - void SendSessionRequest (); - void SendSessionCreated (); - void SendSessionConfirmed (); + bool ProcessSessionConfirmedMessagePart1(const uint8_t *nonce); - 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); + bool ProcessSessionConfirmedMessagePart2(const uint8_t *nonce, uint8_t *m3p2Buf); - // 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); + std::shared_ptr m_EphemeralKeys; + uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 + uint8_t m_RemoteStaticKey[32], m_IV[16]; + i2p::data::IdentHash m_RemoteIdentHash; + uint16_t m3p2Len; - void SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf); - void SendI2NPMsgs (std::vector >& msgs); - void HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > 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 > msgs); + uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], + m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], *m_SessionConfirmedBuffer; + size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; - private: + }; - NTCP2Server& m_Server; - boost::asio::ip::tcp::socket m_Socket; - boost::asio::ip::tcp::endpoint m_RemoteEndpoint; - bool m_IsEstablished, m_IsTerminated; + class NTCP2Server; - std::unique_ptr 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; + class NTCP2Session : public TransportSession, public std::enable_shared_from_this { + public: + + NTCP2Session(NTCP2Server &server, std::shared_ptr in_RemoteRouter = nullptr, + std::shared_ptr 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 > &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 > &msgs); + + void HandleI2NPMsgsSent(const boost::system::error_code &ecode, std::size_t bytes_transferred, + std::vector > 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 > 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 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 - EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; + EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; #else - const uint8_t * m_SendSipKey, * m_ReceiveSipKey; + const uint8_t *m_SendSipKey, *m_ReceiveSipKey; #endif - uint16_t m_NextReceivedLen; - uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; - size_t m_NextReceivedBufferSize; - union - { - uint8_t buf[8]; - uint16_t key; - } m_ReceiveIV, m_SendIV; - uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber; + uint16_t m_NextReceivedLen; + uint8_t *m_NextReceivedBuffer, *m_NextSendBuffer; + size_t m_NextReceivedBufferSize; + union { + uint8_t buf[8]; + uint16_t key; + } m_ReceiveIV, m_SendIV; + uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber; - i2p::I2NPMessagesHandler m_Handler; + i2p::I2NPMessagesHandler m_Handler; - bool m_IsSending, m_IsReceiving; - std::list > m_SendQueue; - uint64_t m_NextRouterInfoResendTime; // seconds since epoch + bool m_IsSending, m_IsReceiving; + std::list > m_SendQueue; + uint64_t m_NextRouterInfoResendTime; // seconds since epoch - uint16_t m_PaddingSizes[16]; - int m_NextPaddingSize; - }; + uint16_t m_PaddingSizes[16]; + int m_NextPaddingSize; + }; - class NTCP2Server: private i2p::util::RunnableServiceWithWork - { - public: + class NTCP2Server : private i2p::util::RunnableServiceWithWork { + public: - enum ProxyType - { - eNoProxy, - eSocksProxy, - eHTTPProxy - }; + enum ProxyType { + eNoProxy, + eSocksProxy, + eHTTPProxy + }; - NTCP2Server (); - ~NTCP2Server (); + NTCP2Server(); - void Start (); - void Stop (); - boost::asio::io_service& GetService () { return GetIOService (); }; + ~NTCP2Server(); - bool AddNTCP2Session (std::shared_ptr session, bool incoming = false); - void RemoveNTCP2Session (std::shared_ptr session); - std::shared_ptr FindNTCP2Session (const i2p::data::IdentHash& ident); + void Start(); - void ConnectWithProxy (std::shared_ptr conn); - void Connect(std::shared_ptr conn); + void Stop(); - bool UsingProxy() const { return m_ProxyType != eNoProxy; }; - void UseProxy(ProxyType proxy, const std::string& address, uint16_t port, const std::string& user, const std::string& pass); + boost::asio::io_service &GetService() { return GetIOService(); }; - void SetLocalAddress (const boost::asio::ip::address& localAddress); + bool AddNTCP2Session(std::shared_ptr session, bool incoming = false); - private: + void RemoveNTCP2Session(std::shared_ptr session); - void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); - void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); + std::shared_ptr FindNTCP2Session(const i2p::data::IdentHash &ident); - void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); - void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); - void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer); + void ConnectWithProxy(std::shared_ptr conn); - // timer - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); + void Connect(std::shared_ptr conn); - private: + bool UsingProxy() const { return m_ProxyType != eNoProxy; }; - boost::asio::deadline_timer m_TerminationTimer; - std::unique_ptr m_NTCP2Acceptor, m_NTCP2V6Acceptor; - std::map > m_NTCP2Sessions; - std::list > m_PendingIncomingSessions; + void UseProxy(ProxyType proxy, const std::string &address, uint16_t port, const std::string &user, + const std::string &pass); - ProxyType m_ProxyType; - std::string m_ProxyAddress, m_ProxyAuthorization; - uint16_t m_ProxyPort; - boost::asio::ip::tcp::resolver m_Resolver; - std::unique_ptr m_ProxyEndpoint; - std::shared_ptr m_Address4, m_Address6, m_YggdrasilAddress; + void SetLocalAddress(const boost::asio::ip::address &localAddress); - public: + private: - // for HTTP/I2PControl - const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; }; - }; -} + void HandleAccept(std::shared_ptr conn, const boost::system::error_code &error); + + void HandleAcceptV6(std::shared_ptr conn, const boost::system::error_code &error); + + void HandleConnect(const boost::system::error_code &ecode, std::shared_ptr conn, + std::shared_ptr timer); + + void HandleProxyConnect(const boost::system::error_code &ecode, std::shared_ptr conn, + std::shared_ptr timer); + + void + AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer); + + // timer + void ScheduleTermination(); + + void HandleTerminationTimer(const boost::system::error_code &ecode); + + private: + + boost::asio::deadline_timer m_TerminationTimer; + std::unique_ptr m_NTCP2Acceptor, m_NTCP2V6Acceptor; + std::map > m_NTCP2Sessions; + std::list > 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 m_ProxyEndpoint; + std::shared_ptr m_Address4, m_Address6, m_YggdrasilAddress; + + public: + + // for HTTP/I2PControl + const decltype(m_NTCP2Sessions) + & + + GetNTCP2Sessions() const { return m_NTCP2Sessions; }; + }; + } } #endif diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 7f31fc7d..44d914db 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -30,1430 +30,1257 @@ using namespace i2p::transport; -namespace i2p -{ -namespace data -{ - NetDb netdb; - - NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true), m_HiddenMode(false) - { - } - - NetDb::~NetDb () - { - Stop (); - delete m_Reseeder; - } - - void NetDb::Start () - { - m_Storage.SetPlace(i2p::fs::GetDataDir()); - m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64); - InitProfilesStorage (); - m_Families.LoadCertificates (); - Load (); - - uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); - if (m_RouterInfos.size () < threshold || m_Floodfills.size () < NETDB_MIN_FLOODFILLS) // reseed if # of router less than threshold or too few floodfiils - { - Reseed (); - } - else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false)) - Reseed (); // we don't have a router we can connect to. Trying to reseed - - auto it = m_RouterInfos.find (i2p::context.GetIdentHash ()); - if (it != m_RouterInfos.end ()) - { - // remove own router - m_Floodfills.remove (it->second); - m_RouterInfos.erase (it); - } - // insert own router - m_RouterInfos.emplace (i2p::context.GetIdentHash (), i2p::context.GetSharedRouterInfo ()); - if (i2p::context.IsFloodfill ()) - m_Floodfills.push_back (i2p::context.GetSharedRouterInfo ()); - - i2p::config::GetOption("persist.profiles", m_PersistProfiles); - - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&NetDb::Run, this)); - } - - void NetDb::Stop () - { - if (m_IsRunning) - { - if (m_PersistProfiles) - for (auto& it: m_RouterInfos) - it.second->SaveProfile (); - DeleteObsoleteProfiles (); - m_RouterInfos.clear (); - m_Floodfills.clear (); - if (m_Thread) - { - m_IsRunning = false; - m_Queue.WakeUp (); - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - m_LeaseSets.clear(); - m_Requests.Stop (); - } - } - - void NetDb::Run () - { - i2p::util::SetThreadName("NetDB"); - - uint64_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0; - uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch (); - int16_t profilesCleanupVariance = 0; - - while (m_IsRunning) - { - try - { - auto msg = m_Queue.GetNextWithTimeout (15000); // 15 sec - if (msg) - { - int numMsgs = 0; - while (msg) - { - LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); - switch (msg->GetTypeID ()) - { - case eI2NPDatabaseStore: - HandleDatabaseStoreMsg (msg); - break; - case eI2NPDatabaseSearchReply: - HandleDatabaseSearchReplyMsg (msg); - break; - case eI2NPDatabaseLookup: - HandleDatabaseLookupMsg (msg); - break; - case eI2NPDeliveryStatus: - HandleDeliveryStatusMsg (msg); - break; - case eI2NPDummyMsg: - // plain RouterInfo from NTCP2 with flags for now - HandleNTCP2RouterInfoMsg (msg); - break; - default: // WTF? - LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); - //i2p::HandleI2NPMessage (msg); - } - if (numMsgs > 100) break; - msg = m_Queue.Get (); - numMsgs++; - } - } - if (!m_IsRunning) break; - if (!i2p::transport::transports.IsOnline ()) continue; // don't manage netdb when offline - - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastManageRequest >= 15) // manage requests every 15 seconds - { - m_Requests.ManageRequests (); - lastManageRequest = ts; - } - - if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute - { - if (lastSave) - { - SaveUpdated (); - ManageLeaseSets (); - } - lastSave = ts; - } - - if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - i2p::context.CleanupDestination (); - lastDestinationCleanup = ts; - } - - if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance)) - { - DeleteObsoleteProfiles (); - lastProfilesCleanup = ts; - profilesCleanupVariance = (rand () % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE); - } - - // publish - if (!m_HiddenMode && i2p::transport::transports.IsOnline ()) - { - bool publish = false; - if (m_PublishReplyToken) - { - // next publishing attempt - if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true; - } - else if (i2p::context.GetLastUpdateTime () > lastPublish || - ts - lastPublish >= NETDB_PUBLISH_INTERVAL) - { - // new publish - m_PublishExcluded.clear (); - if (i2p::context.IsFloodfill ()) - m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // do publish to ourselves - publish = true; - } - if (publish) // update timestamp and publish - { - i2p::context.UpdateTimestamp (ts); - Publish (); - lastPublish = ts; - } - } - - if (ts - lastExploratory >= 30) // exploratory every 30 seconds - { - auto numRouters = m_RouterInfos.size (); - if (!numRouters) - throw std::runtime_error("No known routers, reseed seems to be totally failed"); - else // we have peers now - m_FloodfillBootstrap = nullptr; - if (numRouters < 2500 || ts - lastExploratory >= 90) - { - numRouters = 800/numRouters; - if (numRouters < 1) numRouters = 1; - if (numRouters > 9) numRouters = 9; - m_Requests.ManageRequests (); - if(!m_HiddenMode) - Explore (numRouters); - lastExploratory = ts; - } - } - } - catch (std::exception& ex) - { - LogPrint (eLogError, "NetDb: Runtime exception: ", ex.what ()); - } - } - } - - void NetDb::SetHidden(bool hide) - { - // TODO: remove reachable addresses from router info - m_HiddenMode = hide; - } - - std::shared_ptr NetDb::AddRouterInfo (const uint8_t * buf, int len) - { - bool updated; - return AddRouterInfo (buf, len, updated); - } - - std::shared_ptr NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated) - { - IdentityEx identity; - if (identity.FromBuffer (buf, len)) - return AddRouterInfo (identity.GetIdentHash (), buf, len, updated); - updated = false; - return nullptr; - } - - bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len) - { - bool updated; - AddRouterInfo (ident, buf, len, updated); - return updated; - } - - std::shared_ptr NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated) - { - updated = true; - auto r = FindRouter (ident); - if (r) - { - if (r->IsNewer (buf, len)) - { - bool wasFloodfill = r->IsFloodfill (); - { - std::unique_lock l(m_RouterInfosMutex); - r->Update (buf, len); - } - LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); - if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated - { - LogPrint (eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64()); - std::unique_lock l(m_FloodfillsMutex); - if (wasFloodfill) - m_Floodfills.remove (r); - else if (r->IsEligibleFloodfill ()) - m_Floodfills.push_back (r); - } - } - else - { - LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); - updated = false; - } - } - else - { - r = std::make_shared (buf, len); - if (!r->IsUnreachable () && r->HasValidAddresses ()) - { - bool inserted = false; - { - std::unique_lock l(m_RouterInfosMutex); - inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second; - } - if (inserted) - { - LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); - if (r->IsFloodfill () && r->IsEligibleFloodfill ()) - { - std::unique_lock l(m_FloodfillsMutex); - m_Floodfills.push_back (r); - } - } - else - { - LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64()); - updated = false; - } - } - else - updated = false; - } - // take care about requested destination - m_Requests.RequestComplete (ident, r); - return r; - } - - bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len) - { - std::unique_lock lock(m_LeaseSetsMutex); - bool updated = false; - auto it = m_LeaseSets.find(ident); - if (it != m_LeaseSets.end () && it->second->GetStoreType () == i2p::data::NETDB_STORE_TYPE_LEASESET) - { - // we update only is existing LeaseSet is not LeaseSet2 - uint64_t expires; - if(LeaseSetBufferValidate(buf, len, expires)) - { - if(it->second->GetExpirationTime() < expires) - { - it->second->Update (buf, len, false); // signature is verified already - LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32()); - updated = true; - } - else - LogPrint(eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32()); - } - else - LogPrint(eLogError, "NetDb: LeaseSet is invalid: ", ident.ToBase32()); - } - else - { - auto leaseSet = std::make_shared (buf, len, false); // we don't need leases in netdb - if (leaseSet->IsValid ()) - { - LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32()); - m_LeaseSets[ident] = leaseSet; - updated = true; - } - else - LogPrint (eLogError, "NetDb: New LeaseSet validation failed: ", ident.ToBase32()); - } - return updated; - } - - bool NetDb::AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType) - { - std::unique_lock lock(m_LeaseSetsMutex); - auto leaseSet = std::make_shared (storeType, buf, len, false); // we don't need leases in netdb - if (leaseSet->IsValid ()) - { - auto it = m_LeaseSets.find(ident); - if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || - leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) - { - if (leaseSet->IsPublic () && !leaseSet->IsExpired ()) - { - // TODO: implement actual update - LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); - m_LeaseSets[ident] = leaseSet; - return true; - } - else - { - LogPrint (eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32()); - m_LeaseSets.erase (ident); - } - } - } - else - LogPrint (eLogError, "NetDb: New LeaseSet2 validation failed: ", ident.ToBase32()); - return false; - } - - std::shared_ptr NetDb::FindRouter (const IdentHash& ident) const - { - std::unique_lock l(m_RouterInfosMutex); - auto it = m_RouterInfos.find (ident); - if (it != m_RouterInfos.end ()) - return it->second; - else - return nullptr; - } - - std::shared_ptr NetDb::FindLeaseSet (const IdentHash& destination) const - { - std::unique_lock lock(m_LeaseSetsMutex); - auto it = m_LeaseSets.find (destination); - if (it != m_LeaseSets.end ()) - return it->second; - else - return nullptr; - } - - std::shared_ptr NetDb::FindRouterProfile (const IdentHash& ident) const - { - if (!m_PersistProfiles) - return nullptr; - - auto router = FindRouter (ident); - return router ? router->GetProfile () : nullptr; - } - - void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) - { - auto it = m_RouterInfos.find (ident); - if (it != m_RouterInfos.end ()) - return it->second->SetUnreachable (unreachable); - } - - void NetDb::Reseed () - { - if (!m_Reseeder) - { - m_Reseeder = new Reseeder (); - m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification - } - - // try reseeding from floodfill first if specified - std::string riPath; - if(i2p::config::GetOption("reseed.floodfill", riPath)) - { - auto ri = std::make_shared(riPath); - if (ri->IsFloodfill()) - { - const uint8_t * riData = ri->GetBuffer(); - int riLen = ri->GetBufferLen(); - if (!i2p::data::netdb.AddRouterInfo(riData, riLen)) - { - // bad router info - LogPrint(eLogError, "NetDb: Bad router info"); - return; - } - m_FloodfillBootstrap = ri; - ReseedFromFloodfill(*ri); - // don't try reseed servers if trying to bootstrap from floodfill - return; - } - } - - m_Reseeder->Bootstrap (); - } - - void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) - { - LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64()); - std::vector > requests; - - i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash(); - i2p::data::IdentHash ih = ri.GetIdentHash(); - i2p::data::IdentHash randomIdent; - - // make floodfill lookups - while(numFloodfills > 0) { - randomIdent.Randomize(); - auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false); - requests.push_back(msg); - numFloodfills --; - } - - // make regular router lookups - while(numRouters > 0) { - randomIdent.Randomize(); - auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true); - requests.push_back(msg); - numRouters --; - } - - // send them off - i2p::transport::transports.SendMessages(ih, requests); - } - - bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts) - { - auto r = std::make_shared(path); - if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses () && - ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL) - { - r->DeleteBuffer (); - if (m_RouterInfos.emplace (r->GetIdentHash (), r).second) - { - if (r->IsFloodfill () && r->IsEligibleFloodfill ()) - m_Floodfills.push_back (r); - } - } - else - { - LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid or too old. Delete"); - i2p::fs::Remove(path); - } - return true; - } - - void NetDb::VisitLeaseSets(LeaseSetVisitor v) - { - std::unique_lock lock(m_LeaseSetsMutex); - for ( auto & entry : m_LeaseSets) - v(entry.first, entry.second); - } - - void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v) - { - m_Storage.Iterate([v] (const std::string & filename) - { - auto ri = std::make_shared(filename); - v(ri); - }); - } - - void NetDb::VisitRouterInfos(RouterInfoVisitor v) - { - std::unique_lock lock(m_RouterInfosMutex); - for ( const auto & item : m_RouterInfos ) - v(item.second); - } - - size_t NetDb::VisitRandomRouterInfos(RouterInfoFilter filter, RouterInfoVisitor v, size_t n) - { - std::vector > found; - const size_t max_iters_per_cyle = 3; - size_t iters = max_iters_per_cyle; - while(n > 0) - { - std::unique_lock lock(m_RouterInfosMutex); - uint32_t idx = rand () % m_RouterInfos.size (); - uint32_t i = 0; - for (const auto & it : m_RouterInfos) { - if(i >= idx) // are we at the random start point? - { - // yes, check if we want this one - if(filter(it.second)) - { - // we have a match - --n; - found.push_back(it.second); - // reset max iterations per cycle - iters = max_iters_per_cyle; - break; - } - } - else // not there yet - ++i; - } - // we have enough - if(n == 0) break; - --iters; - // have we tried enough this cycle ? - if(!iters) { - // yes let's try the next cycle - --n; - iters = max_iters_per_cyle; - } - } - // visit the ones we found - size_t visited = 0; - for(const auto & ri : found ) { - v(ri); - ++visited; - } - return visited; - } - - void NetDb::Load () - { - // make sure we cleanup netDb from previous attempts - m_RouterInfos.clear (); - m_Floodfills.clear (); - - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); - std::vector files; - m_Storage.Traverse(files); - for (const auto& path : files) - LoadRouterInfo (path, ts); - - LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)"); - } - - void NetDb::SaveUpdated () - { - int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0; - auto total = m_RouterInfos.size (); - auto totalFloodfills = m_Floodfills.size (); - uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); - auto uptime = i2p::context.GetUptime (); - // routers don't expire if less than 90 or uptime is less than 1 hour - bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes - if (checkForExpiration && uptime > 3600) // 1 hour - expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : - NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; - - auto own = i2p::context.GetSharedRouterInfo (); - for (auto& it: m_RouterInfos) - { - if (it.second == own) continue; // skip own - std::string ident = it.second->GetIdentHashBase64(); - if (it.second->IsUpdated ()) - { - it.second->SaveToFile (m_Storage.Path(ident)); - it.second->SetUpdated (false); - it.second->SetUnreachable (false); - it.second->DeleteBuffer (); - updatedCount++; - continue; - } - // make router reachable back if too few routers or floodfills - if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || - (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) - it.second->SetUnreachable (false); - // find & mark expired routers - if (!it.second->IsReachable () && it.second->IsSSU (false)) - { - if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL) - // RouterInfo expires after 1 hour if uses introducer - it.second->SetUnreachable (true); - } - else if (checkForExpiration && ts > it.second->GetTimestamp () + expirationTimeout) - it.second->SetUnreachable (true); - - if (it.second->IsUnreachable ()) - { - if (it.second->IsFloodfill ()) deletedFloodfillsCount++; - // delete RI file - m_Storage.Remove(ident); - deletedCount++; - if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; - } - } // m_RouterInfos iteration - - m_RouterInfoBuffersPool.CleanUpMt (); - - if (updatedCount > 0) - LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers"); - if (deletedCount > 0) - { - LogPrint (eLogInfo, "NetDb: Deleting ", deletedCount, " unreachable routers"); - // clean up RouterInfos table - { - std::unique_lock l(m_RouterInfosMutex); - for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) - { - if (it->second->IsUnreachable ()) - { - if (m_PersistProfiles) it->second->SaveProfile (); - it = m_RouterInfos.erase (it); - continue; - } - ++it; - } - } - // clean up expired floodfills or not floodfills anymore - { - std::unique_lock l(m_FloodfillsMutex); - for (auto it = m_Floodfills.begin (); it != m_Floodfills.end ();) - if ((*it)->IsUnreachable () || !(*it)->IsFloodfill ()) - it = m_Floodfills.erase (it); - else - ++it; - } - } - } - - void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct) - { - auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory - if (!dest) - { - LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); - return; - } - - auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); - if (floodfill) - { - if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && - !i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) - direct = false; // floodfill can't be reached directly - if (direct) - transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); - else - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr; - auto inbound = pool ? pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr; - if (outbound && inbound) - outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, dest->CreateRequestMessage (floodfill, inbound)); - else - { - LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no tunnels found"); - m_Requests.RequestComplete (destination, nullptr); - } - } - } - else - { - LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no floodfills found"); - m_Requests.RequestComplete (destination, nullptr); - } - } - - void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete) - { - - auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory - if (!dest) - { - LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); - return; - } - LogPrint(eLogInfo, "NetDb: Destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64()); - // direct - transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr)); - } - - void NetDb::HandleNTCP2RouterInfoMsg (std::shared_ptr m) - { - uint8_t flood = m->GetPayload ()[0] & NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD; - bool updated; - auto ri = AddRouterInfo (m->GetPayload () + 1, m->GetPayloadLength () - 1, updated); // without flags - if (flood && updated && context.IsFloodfill () && ri) - { - auto floodMsg = CreateDatabaseStoreMsg (ri, 0); // replyToken = 0 - Flood (ri->GetIdentHash (), floodMsg); - } - } - - void NetDb::HandleDatabaseStoreMsg (std::shared_ptr m) - { - const uint8_t * buf = m->GetPayload (); - size_t len = m->GetSize (); - IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); - if (ident.IsZero ()) - { - LogPrint (eLogDebug, "NetDb: Database store with zero ident, dropped"); - return; - } - uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); - size_t offset = DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); - uint32_t tunnelID = bufbe32toh (buf + offset); - offset += 4; - if (!tunnelID) // send response directly - transports.SendMessage (buf + offset, deliveryStatus); - else - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus); - else - LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); - } - offset += 32; - } - // we must send reply back before this check - if (ident == i2p::context.GetIdentHash ()) - { - LogPrint (eLogDebug, "NetDb: Database store with own RouterInfo received, dropped"); - return; - } - size_t payloadOffset = offset; - - bool updated = false; - uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET]; - if (storeType) // LeaseSet or LeaseSet2 - { - if (!m->from) // unsolicited LS must be received directly - { - if (storeType == NETDB_STORE_TYPE_LEASESET) // 1 - { - LogPrint (eLogDebug, "NetDb: Store request: LeaseSet for ", ident.ToBase32()); - updated = AddLeaseSet (ident, buf + offset, len - offset); - } - else // all others are considered as LeaseSet2 - { - LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", storeType, " for ", ident.ToBase32()); - updated = AddLeaseSet2 (ident, buf + offset, len - offset, storeType); - } - } - } - else // RouterInfo - { - LogPrint (eLogDebug, "NetDb: Store request: RouterInfo"); - size_t size = bufbe16toh (buf + offset); - offset += 2; - if (size > MAX_RI_BUFFER_SIZE || size > len - offset) - { - LogPrint (eLogError, "NetDb: Invalid RouterInfo length ", (int)size); - return; - } - uint8_t uncompressed[MAX_RI_BUFFER_SIZE]; - size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, MAX_RI_BUFFER_SIZE); - if (uncompressedSize && uncompressedSize < MAX_RI_BUFFER_SIZE) - updated = AddRouterInfo (ident, uncompressed, uncompressedSize); - else - { - LogPrint (eLogInfo, "NetDb: Decompression failed ", uncompressedSize); - return; - } - } - - if (replyToken && context.IsFloodfill () && updated) - { - // flood updated - auto floodMsg = NewI2NPShortMessage (); - uint8_t * payload = floodMsg->GetPayload (); - memcpy (payload, buf, 33); // key + type - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token - size_t msgLen = len - payloadOffset; - floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen; - if (floodMsg->len < floodMsg->maxLen) - { - memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen); - floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore); - Flood (ident, floodMsg); - } - else - LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len); - } - } - - void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - char key[48]; - int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); - key[l] = 0; - int num = buf[32]; // num - LogPrint (eLogDebug, "NetDb: DatabaseSearchReply for ", key, " num=", num); - IdentHash ident (buf); - auto dest = m_Requests.FindRequest (ident); - if (dest) - { - bool deleteDest = true; - if (num > 0) - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr; - if (!dest->IsExploratory ()) - { - // reply to our destination. Try other floodfills - if (outbound && inbound) - { - auto count = dest->GetExcludedPeers ().size (); - if (count < 7) - { - auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); - if (nextFloodfill) - { - // request destination - LogPrint (eLogDebug, "NetDb: Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); - outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (nextFloodfill, inbound)); - deleteDest = false; - } - } - else - LogPrint (eLogWarning, "NetDb: ", key, " was not found on ", count, " floodfills"); - } - } - - if (deleteDest) - // no more requests for the destinationation. delete it - m_Requests.RequestComplete (ident, nullptr); - } - else - // no more requests for destination possible. delete it - m_Requests.RequestComplete (ident, nullptr); - } - else if(!m_FloodfillBootstrap) - LogPrint (eLogWarning, "NetDb: Requested destination for ", key, " not found"); - - // try responses - for (int i = 0; i < num; i++) - { - const uint8_t * router = buf + 33 + i*32; - char peerHash[48]; - int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); - peerHash[l1] = 0; - LogPrint (eLogDebug, "NetDb: ", i, ": ", peerHash); - - auto r = FindRouter (router); - if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL) - { - // router with ident not found or too old (1 hour) - LogPrint (eLogDebug, "NetDb: Found new/outdated router. Requesting RouterInfo..."); - if(m_FloodfillBootstrap) - RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true); - else - RequestDestination (router); - } - else - LogPrint (eLogDebug, "NetDb: [:|||:]"); - } - } - - void NetDb::HandleDatabaseLookupMsg (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - IdentHash ident (buf); - if (ident.IsZero ()) - { - LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored"); - return; - } - char key[48]; - int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); - key[l] = 0; - - IdentHash replyIdent(buf + 32); - uint8_t flag = buf[64]; - - - LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag); - uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; - const uint8_t * excluded = buf + 65; - uint32_t replyTunnelID = 0; - if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel - { - replyTunnelID = bufbe32toh (excluded); - excluded += 4; - } - uint16_t numExcluded = bufbe16toh (excluded); - excluded += 2; - if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ()) - { - LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much"); - return; - } - - std::shared_ptr replyMsg; - if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) - { - LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); - std::set excludedRouters; - const uint8_t * excluded_ident = excluded; - for (int i = 0; i < numExcluded; i++) - { - excludedRouters.insert (excluded_ident); - excluded_ident += 32; - } - std::vector routers; - for (int i = 0; i < 3; i++) - { - auto r = GetClosestNonFloodfill (ident, excludedRouters); - if (r) - { - routers.push_back (r->GetIdentHash ()); - excludedRouters.insert (r->GetIdentHash ()); - } - } - replyMsg = CreateDatabaseSearchReply (ident, routers); - } - else - { - if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || - lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) - { - auto router = FindRouter (ident); - if (router) - { - LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found"); - PopulateRouterInfoBuffer (router); - if (router->GetBuffer ()) - replyMsg = CreateDatabaseStoreMsg (router); - } - } - - if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || - lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) - { - auto leaseSet = FindLeaseSet (ident); - if (!leaseSet) - { - // no lease set found - LogPrint(eLogDebug, "NetDb: Requested LeaseSet not found for ", ident.ToBase32()); - } - else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets - { - LogPrint (eLogDebug, "NetDb: Requested LeaseSet ", key, " found"); - replyMsg = CreateDatabaseStoreMsg (ident, leaseSet); - } - } - - if (!replyMsg) - { - std::set excludedRouters; - const uint8_t * exclude_ident = excluded; - for (int i = 0; i < numExcluded; i++) - { - excludedRouters.insert (exclude_ident); - exclude_ident += 32; - } - auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true); - if (closestFloodfills.empty ()) - LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded"); - replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills); - } - } - excluded += numExcluded * 32; - if (replyMsg) - { - if (replyTunnelID) - { - // encryption might be used though tunnel only - if (flag & (DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_ECIES_FLAG)) // encrypted reply requested - { - const uint8_t * sessionKey = excluded; - const uint8_t numTags = excluded[32]; - if (numTags) - { - if (flag & DATABASE_LOOKUP_ECIES_FLAG) - { - uint64_t tag; - memcpy (&tag, excluded + 33, 8); - replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag); - } - else - { - const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag - i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag); - replyMsg = garlic.WrapSingleMessage (replyMsg); - } - if (!replyMsg) - LogPrint (eLogError, "NetDb: Failed to wrap message"); - } - else - LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided"); - } - auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsg (replyIdent, replyTunnelID, replyMsg); - else - transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg)); - } - else - transports.SendMessage (replyIdent, replyMsg); - } - } - - void NetDb::HandleDeliveryStatusMsg (std::shared_ptr msg) - { - if (m_PublishReplyToken == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET)) - { - LogPrint (eLogInfo, "NetDb: Publishing confirmed. reply token=", m_PublishReplyToken); - m_PublishExcluded.clear (); - m_PublishReplyToken = 0; - } - } - - void NetDb::Explore (int numDestinations) - { - // new requests - auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; - auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; - bool throughTunnels = outbound && inbound; - - uint8_t randomHash[32]; - std::vector msgs; - LogPrint (eLogInfo, "NetDb: Exploring new ", numDestinations, " routers ..."); - for (int i = 0; i < numDestinations; i++) - { - RAND_bytes (randomHash, 32); - auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory - if (!dest) - { - LogPrint (eLogWarning, "NetDb: Exploratory destination is requested already"); - return; - } - auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); - if (floodfill) - { - if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) - throughTunnels = false; - if (throughTunnels) - { - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - floodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg () // tell floodfill about us - }); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - floodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (floodfill, inbound) // explore - }); - } - else - i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); - } - else - m_Requests.RequestComplete (randomHash, nullptr); - } - if (throughTunnels && msgs.size () > 0) - outbound->SendTunnelDataMsg (msgs); - } - - void NetDb::Publish () - { - i2p::context.UpdateStats (); // for floodfill - - if (m_PublishExcluded.size () > NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS) - { - LogPrint (eLogError, "NetDb: Couldn't publish our RouterInfo to ", NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS, " closest routers. Try again"); - m_PublishExcluded.clear (); - } - - auto floodfill = GetClosestFloodfill (i2p::context.GetIdentHash (), m_PublishExcluded); - if (floodfill) - { - uint32_t replyToken; - RAND_bytes ((uint8_t *)&replyToken, 4); - LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken); - m_PublishExcluded.insert (floodfill->GetIdentHash ()); - m_PublishReplyToken = replyToken; - if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect? - i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ? - // send directly - transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); - else - { - // otherwise through exploratory - auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr; - auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr; - if (inbound && outbound) - outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound)); - } - } - } - - void NetDb::Flood (const IdentHash& ident, std::shared_ptr floodMsg) - { - std::set excluded; - excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself - excluded.insert (ident); // don't flood back - for (int i = 0; i < 3; i++) - { - auto floodfill = GetClosestFloodfill (ident, excluded); - if (floodfill) - { - auto h = floodfill->GetIdentHash(); - LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64()); - transports.SendMessage (h, CopyI2NPMessage(floodMsg)); - excluded.insert (h); - } - else - break; - } - } - - std::shared_ptr NetDb::GetRandomRouter () const - { - return GetRandomRouter ( - [](std::shared_ptr router)->bool - { - return !router->IsHidden (); - }); - } - - std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const - { - return GetRandomRouter ( - [compatibleWith, reverse](std::shared_ptr router)->bool - { - return !router->IsHidden () && router != compatibleWith && - (reverse ? compatibleWith->IsReachableFrom (*router) : - router->IsReachableFrom (*compatibleWith)) && - router->IsECIES (); - }); - } - - std::shared_ptr NetDb::GetRandomPeerTestRouter (bool v4, const std::set& excluded) const - { - return GetRandomRouter ( - [v4, &excluded](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && - router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ()); - }); - } - - std::shared_ptr NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const - { - return GetRandomRouter ( - [v4, &excluded](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && - router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ()); - }); - } - - std::shared_ptr NetDb::GetRandomSSUV6Router () const - { - return GetRandomRouter ( - [](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 (); - }); - } - - std::shared_ptr NetDb::GetRandomIntroducer (bool v4, const std::set& excluded) const - { - return GetRandomRouter ( - [v4, &excluded](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && !router->IsFloodfill () && // floodfills don't send relay tag - router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()); - }); - } - - std::shared_ptr NetDb::GetRandomSSU2Introducer (bool v4, const std::set& excluded) const - { - return GetRandomRouter ( - [v4, &excluded](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsSSU2Introducer (v4) && - !excluded.count (router->GetIdentHash ()); - }); - } - - std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const - { - return GetRandomRouter ( - [compatibleWith, reverse](std::shared_ptr router)->bool - { - return !router->IsHidden () && router != compatibleWith && - (reverse ? compatibleWith->IsReachableFrom (*router) : - router->IsReachableFrom (*compatibleWith)) && - (router->GetCaps () & RouterInfo::eHighBandwidth) && - router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION && - router->IsECIES (); - }); - } - - template - std::shared_ptr NetDb::GetRandomRouter (Filter filter) const - { - if (m_RouterInfos.empty()) - return 0; - uint16_t inds[3]; - RAND_bytes ((uint8_t *)inds, sizeof (inds)); - std::unique_lock l(m_RouterInfosMutex); - inds[0] %= m_RouterInfos.size (); - auto it = m_RouterInfos.begin (); - std::advance (it, inds[0]); - // try random router - if (it != m_RouterInfos.end () && !it->second->IsUnreachable () && filter (it->second)) - return it->second; - // try some routers around - auto it1 = m_RouterInfos.begin (); - if (inds[0]) - { - // before - inds[1] %= inds[0]; - std::advance (it1, (inds[1] + inds[0])/2); - } - else - it1 = it; - auto it2 = it; - if (inds[0] < m_RouterInfos.size () - 1) - { - // after - inds[2] %= (m_RouterInfos.size () - 1 - inds[0]); inds[2] /= 2; - std::advance (it2, inds[2]); - } - // it1 - from, it2 - to - it = it1; - while (it != it2 && it != m_RouterInfos.end ()) - { - if (!it->second->IsUnreachable () && filter (it->second)) - return it->second; - it++; - } - // still not found, try from the beginning - it = m_RouterInfos.begin (); - while (it != it1 && it != m_RouterInfos.end ()) - { - if (!it->second->IsUnreachable () && filter (it->second)) - return it->second; - it++; - } - // still not found, try to the beginning - it = it2; - while (it != m_RouterInfos.end ()) - { - if (!it->second->IsUnreachable () && filter (it->second)) - return it->second; - it++; - } - return nullptr; // seems we have too few routers - } - - void NetDb::PostI2NPMsg (std::shared_ptr msg) - { - if (msg) m_Queue.Put (msg); - } - - std::shared_ptr NetDb::GetClosestFloodfill (const IdentHash& destination, - const std::set& excluded, bool closeThanUsOnly) const - { - std::shared_ptr r; - XORMetric minMetric; - IdentHash destKey = CreateRoutingKey (destination); - if (closeThanUsOnly) - minMetric = destKey ^ i2p::context.GetIdentHash (); - else - minMetric.SetMax (); - std::unique_lock l(m_FloodfillsMutex); - for (const auto& it: m_Floodfills) - { - if (!it->IsUnreachable ()) - { - XORMetric m = destKey ^ it->GetIdentHash (); - if (m < minMetric && !excluded.count (it->GetIdentHash ())) - { - minMetric = m; - r = it; - } - } - } - return r; - } - - std::vector NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num, - std::set& excluded, bool closeThanUsOnly) const - { - struct Sorted - { - std::shared_ptr r; - XORMetric metric; - bool operator< (const Sorted& other) const { return metric < other.metric; }; - }; - - std::set sorted; - IdentHash destKey = CreateRoutingKey (destination); - XORMetric ourMetric; - if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash (); - { - std::unique_lock l(m_FloodfillsMutex); - for (const auto& it: m_Floodfills) - { - if (!it->IsUnreachable ()) - { - XORMetric m = destKey ^ it->GetIdentHash (); - if (closeThanUsOnly && ourMetric < m) continue; - if (sorted.size () < num) - sorted.insert ({it, m}); - else if (m < sorted.rbegin ()->metric) - { - sorted.insert ({it, m}); - sorted.erase (std::prev (sorted.end ())); - } - } - } - } - - std::vector res; - size_t i = 0; - for (const auto& it: sorted) - { - if (i < num) - { - const auto& ident = it.r->GetIdentHash (); - if (!excluded.count (ident)) - { - res.push_back (ident); - i++; - } - } - else - break; - } - return res; - } - - std::shared_ptr NetDb::GetRandomRouterInFamily (FamilyID fam) const - { - return GetRandomRouter( - [fam](std::shared_ptr router)->bool - { - return router->IsFamily(fam); - }); - } - - std::shared_ptr NetDb::GetClosestNonFloodfill (const IdentHash& destination, - const std::set& excluded) const - { - std::shared_ptr r; - XORMetric minMetric; - IdentHash destKey = CreateRoutingKey (destination); - minMetric.SetMax (); - // must be called from NetDb thread only - for (const auto& it: m_RouterInfos) - { - if (!it.second->IsFloodfill ()) - { - XORMetric m = destKey ^ it.first; - if (m < minMetric && !excluded.count (it.first)) - { - minMetric = m; - r = it.second; - } - } - } - return r; - } - - void NetDb::ManageLeaseSets () - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();) - { - if (!it->second->IsValid () || ts > it->second->GetExpirationTime () - LEASE_ENDDATE_THRESHOLD) - { - LogPrint (eLogInfo, "NetDb: LeaseSet ", it->first.ToBase64 (), " expired or invalid"); - it = m_LeaseSets.erase (it); - } - else - ++it; - } - m_LeasesPool.CleanUpMt (); - } - - void NetDb::PopulateRouterInfoBuffer (std::shared_ptr r) - { - if (!r || r->GetBuffer ()) return; - r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ())); - } -} +namespace i2p { + namespace data { + NetDb netdb; + + NetDb::NetDb() : m_IsRunning(false), m_Thread(nullptr), m_Reseeder(nullptr), + m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles(true), m_HiddenMode(false) { + } + + NetDb::~NetDb() { + Stop(); + delete m_Reseeder; + } + + void NetDb::Start() { + m_Storage.SetPlace(i2p::fs::GetDataDir()); + m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64); + InitProfilesStorage(); + m_Families.LoadCertificates(); + Load(); + + uint16_t threshold; + i2p::config::GetOption("reseed.threshold", threshold); + if (m_RouterInfos.size() < threshold || m_Floodfills.size() < + NETDB_MIN_FLOODFILLS) // reseed if # of router less than threshold or too few floodfiils + { + Reseed(); + } else if (!GetRandomRouter(i2p::context.GetSharedRouterInfo(), false)) + Reseed(); // we don't have a router we can connect to. Trying to reseed + + auto it = m_RouterInfos.find(i2p::context.GetIdentHash()); + if (it != m_RouterInfos.end()) { + // remove own router + m_Floodfills.remove(it->second); + m_RouterInfos.erase(it); + } + // insert own router + m_RouterInfos.emplace(i2p::context.GetIdentHash(), i2p::context.GetSharedRouterInfo()); + if (i2p::context.IsFloodfill()) + m_Floodfills.push_back(i2p::context.GetSharedRouterInfo()); + + i2p::config::GetOption("persist.profiles", m_PersistProfiles); + + m_IsRunning = true; + m_Thread = new std::thread(std::bind(&NetDb::Run, this)); + } + + void NetDb::Stop() { + if (m_IsRunning) { + if (m_PersistProfiles) + for (auto &it: m_RouterInfos) + it.second->SaveProfile(); + DeleteObsoleteProfiles(); + m_RouterInfos.clear(); + m_Floodfills.clear(); + if (m_Thread) { + m_IsRunning = false; + m_Queue.WakeUp(); + m_Thread->join(); + delete m_Thread; + m_Thread = 0; + } + m_LeaseSets.clear(); + m_Requests.Stop(); + } + } + + void NetDb::Run() { + i2p::util::SetThreadName("NetDB"); + + uint64_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0; + uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch(); + int16_t profilesCleanupVariance = 0; + + while (m_IsRunning) { + try { + auto msg = m_Queue.GetNextWithTimeout(15000); // 15 sec + if (msg) { + int numMsgs = 0; + while (msg) { + LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID()); + switch (msg->GetTypeID()) { + case eI2NPDatabaseStore: + HandleDatabaseStoreMsg(msg); + break; + case eI2NPDatabaseSearchReply: + HandleDatabaseSearchReplyMsg(msg); + break; + case eI2NPDatabaseLookup: + HandleDatabaseLookupMsg(msg); + break; + case eI2NPDeliveryStatus: + HandleDeliveryStatusMsg(msg); + break; + case eI2NPDummyMsg: + // plain RouterInfo from NTCP2 with flags for now + HandleNTCP2RouterInfoMsg(msg); + break; + default: // WTF? + LogPrint(eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID()); + //i2p::HandleI2NPMessage (msg); + } + if (numMsgs > 100) break; + msg = m_Queue.Get(); + numMsgs++; + } + } + if (!m_IsRunning) break; + if (!i2p::transport::transports.IsOnline()) continue; // don't manage netdb when offline + + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + if (ts - lastManageRequest >= 15) // manage requests every 15 seconds + { + m_Requests.ManageRequests(); + lastManageRequest = ts; + } + + if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute + { + if (lastSave) { + SaveUpdated(); + ManageLeaseSets(); + } + lastSave = ts; + } + + if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT) { + i2p::context.CleanupDestination(); + lastDestinationCleanup = ts; + } + + if (ts - lastProfilesCleanup >= + (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance)) { + DeleteObsoleteProfiles(); + lastProfilesCleanup = ts; + profilesCleanupVariance = (rand() % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - + i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE); + } + + // publish + if (!m_HiddenMode && i2p::transport::transports.IsOnline()) { + bool publish = false; + if (m_PublishReplyToken) { + // next publishing attempt + if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true; + } else if (i2p::context.GetLastUpdateTime() > lastPublish || + ts - lastPublish >= NETDB_PUBLISH_INTERVAL) { + // new publish + m_PublishExcluded.clear(); + if (i2p::context.IsFloodfill()) + m_PublishExcluded.insert(i2p::context.GetIdentHash()); // do publish to ourselves + publish = true; + } + if (publish) // update timestamp and publish + { + i2p::context.UpdateTimestamp(ts); + Publish(); + lastPublish = ts; + } + } + + if (ts - lastExploratory >= 30) // exploratory every 30 seconds + { + auto numRouters = m_RouterInfos.size(); + if (!numRouters) + throw std::runtime_error("No known routers, reseed seems to be totally failed"); + else // we have peers now + m_FloodfillBootstrap = nullptr; + if (numRouters < 2500 || ts - lastExploratory >= 90) { + numRouters = 800 / numRouters; + if (numRouters < 1) numRouters = 1; + if (numRouters > 9) numRouters = 9; + m_Requests.ManageRequests(); + if (!m_HiddenMode) + Explore(numRouters); + lastExploratory = ts; + } + } + } + catch (std::exception &ex) { + LogPrint(eLogError, "NetDb: Runtime exception: ", ex.what()); + } + } + } + + void NetDb::SetHidden(bool hide) { + // TODO: remove reachable addresses from router info + m_HiddenMode = hide; + } + + std::shared_ptr NetDb::AddRouterInfo(const uint8_t *buf, int len) { + bool updated; + return AddRouterInfo(buf, len, updated); + } + + std::shared_ptr NetDb::AddRouterInfo(const uint8_t *buf, int len, bool &updated) { + IdentityEx identity; + if (identity.FromBuffer(buf, len)) + return AddRouterInfo(identity.GetIdentHash(), buf, len, updated); + updated = false; + return nullptr; + } + + bool NetDb::AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len) { + bool updated; + AddRouterInfo(ident, buf, len, updated); + return updated; + } + + std::shared_ptr + NetDb::AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len, bool &updated) { + updated = true; + auto r = FindRouter(ident); + if (r) { + if (r->IsNewer(buf, len)) { + bool wasFloodfill = r->IsFloodfill(); + { + std::unique_lock l(m_RouterInfosMutex); + r->Update(buf, len); + } + LogPrint(eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); + if (wasFloodfill != r->IsFloodfill()) // if floodfill status updated + { + LogPrint(eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64()); + std::unique_lock l(m_FloodfillsMutex); + if (wasFloodfill) + m_Floodfills.remove(r); + else if (r->IsEligibleFloodfill()) + m_Floodfills.push_back(r); + } + } else { + LogPrint(eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); + updated = false; + } + } else { + r = std::make_shared(buf, len); + if (!r->IsUnreachable() && r->HasValidAddresses()) { + bool inserted = false; + { + std::unique_lock l(m_RouterInfosMutex); + inserted = m_RouterInfos.insert({r->GetIdentHash(), r}).second; + } + if (inserted) { + LogPrint(eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); + if (r->IsFloodfill() && r->IsEligibleFloodfill()) { + std::unique_lock l(m_FloodfillsMutex); + m_Floodfills.push_back(r); + } + } else { + LogPrint(eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64()); + updated = false; + } + } else + updated = false; + } + // take care about requested destination + m_Requests.RequestComplete(ident, r); + return r; + } + + bool NetDb::AddLeaseSet(const IdentHash &ident, const uint8_t *buf, int len) { + std::unique_lock lock(m_LeaseSetsMutex); + bool updated = false; + auto it = m_LeaseSets.find(ident); + if (it != m_LeaseSets.end() && it->second->GetStoreType() == i2p::data::NETDB_STORE_TYPE_LEASESET) { + // we update only is existing LeaseSet is not LeaseSet2 + uint64_t expires; + if (LeaseSetBufferValidate(buf, len, expires)) { + if (it->second->GetExpirationTime() < expires) { + it->second->Update(buf, len, false); // signature is verified already + LogPrint(eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32()); + updated = true; + } else + LogPrint(eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32()); + } else + LogPrint(eLogError, "NetDb: LeaseSet is invalid: ", ident.ToBase32()); + } else { + auto leaseSet = std::make_shared(buf, len, false); // we don't need leases in netdb + if (leaseSet->IsValid()) { + LogPrint(eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32()); + m_LeaseSets[ident] = leaseSet; + updated = true; + } else + LogPrint(eLogError, "NetDb: New LeaseSet validation failed: ", ident.ToBase32()); + } + return updated; + } + + bool NetDb::AddLeaseSet2(const IdentHash &ident, const uint8_t *buf, int len, uint8_t storeType) { + std::unique_lock lock(m_LeaseSetsMutex); + auto leaseSet = std::make_shared(storeType, buf, len, false); // we don't need leases in netdb + if (leaseSet->IsValid()) { + auto it = m_LeaseSets.find(ident); + if (it == m_LeaseSets.end() || it->second->GetStoreType() != storeType || + leaseSet->GetPublishedTimestamp() > it->second->GetPublishedTimestamp()) { + if (leaseSet->IsPublic() && !leaseSet->IsExpired()) { + // TODO: implement actual update + LogPrint(eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); + m_LeaseSets[ident] = leaseSet; + return true; + } else { + LogPrint(eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32()); + m_LeaseSets.erase(ident); + } + } + } else + LogPrint(eLogError, "NetDb: New LeaseSet2 validation failed: ", ident.ToBase32()); + return false; + } + + std::shared_ptr NetDb::FindRouter(const IdentHash &ident) const { + std::unique_lock l(m_RouterInfosMutex); + auto it = m_RouterInfos.find(ident); + if (it != m_RouterInfos.end()) + return it->second; + else + return nullptr; + } + + std::shared_ptr NetDb::FindLeaseSet(const IdentHash &destination) const { + std::unique_lock lock(m_LeaseSetsMutex); + auto it = m_LeaseSets.find(destination); + if (it != m_LeaseSets.end()) + return it->second; + else + return nullptr; + } + + std::shared_ptr NetDb::FindRouterProfile(const IdentHash &ident) const { + if (!m_PersistProfiles) + return nullptr; + + auto router = FindRouter(ident); + return router ? router->GetProfile() : nullptr; + } + + void NetDb::SetUnreachable(const IdentHash &ident, bool unreachable) { + auto it = m_RouterInfos.find(ident); + if (it != m_RouterInfos.end()) + return it->second->SetUnreachable(unreachable); + } + + void NetDb::Reseed() { + if (!m_Reseeder) { + m_Reseeder = new Reseeder(); + m_Reseeder->LoadCertificates(); // we need certificates for SU3 verification + } + + // try reseeding from floodfill first if specified + std::string riPath; + if (i2p::config::GetOption("reseed.floodfill", riPath)) { + auto ri = std::make_shared(riPath); + if (ri->IsFloodfill()) { + const uint8_t *riData = ri->GetBuffer(); + int riLen = ri->GetBufferLen(); + if (!i2p::data::netdb.AddRouterInfo(riData, riLen)) { + // bad router info + LogPrint(eLogError, "NetDb: Bad router info"); + return; + } + m_FloodfillBootstrap = ri; + ReseedFromFloodfill(*ri); + // don't try reseed servers if trying to bootstrap from floodfill + return; + } + } + + m_Reseeder->Bootstrap(); + } + + void NetDb::ReseedFromFloodfill(const RouterInfo &ri, int numRouters, int numFloodfills) { + LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64()); + std::vector > requests; + + i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash(); + i2p::data::IdentHash ih = ri.GetIdentHash(); + i2p::data::IdentHash randomIdent; + + // make floodfill lookups + while (numFloodfills > 0) { + randomIdent.Randomize(); + auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false); + requests.push_back(msg); + numFloodfills--; + } + + // make regular router lookups + while (numRouters > 0) { + randomIdent.Randomize(); + auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true); + requests.push_back(msg); + numRouters--; + } + + // send them off + i2p::transport::transports.SendMessages(ih, requests); + } + + bool NetDb::LoadRouterInfo(const std::string &path, uint64_t ts) { + auto r = std::make_shared(path); + if (r->GetRouterIdentity() && !r->IsUnreachable() && r->HasValidAddresses() && + ts < r->GetTimestamp() + 24 * 60 * 60 * NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT * 1000LL) { + r->DeleteBuffer(); + if (m_RouterInfos.emplace(r->GetIdentHash(), r).second) { + if (r->IsFloodfill() && r->IsEligibleFloodfill()) + m_Floodfills.push_back(r); + } + } else { + LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid or too old. Delete"); + i2p::fs::Remove(path); + } + return true; + } + + void NetDb::VisitLeaseSets(LeaseSetVisitor v) { + std::unique_lock lock(m_LeaseSetsMutex); + for (auto &entry: m_LeaseSets) + v(entry.first, entry.second); + } + + void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v) { + m_Storage.Iterate([v](const std::string &filename) { + auto ri = std::make_shared(filename); + v(ri); + }); + } + + void NetDb::VisitRouterInfos(RouterInfoVisitor v) { + std::unique_lock lock(m_RouterInfosMutex); + for (const auto &item: m_RouterInfos) + v(item.second); + } + + size_t NetDb::VisitRandomRouterInfos(RouterInfoFilter filter, RouterInfoVisitor v, size_t n) { + std::vector > found; + const size_t max_iters_per_cyle = 3; + size_t iters = max_iters_per_cyle; + while (n > 0) { + std::unique_lock lock(m_RouterInfosMutex); + uint32_t idx = rand() % m_RouterInfos.size(); + uint32_t i = 0; + for (const auto &it: m_RouterInfos) { + if (i >= idx) // are we at the random start point? + { + // yes, check if we want this one + if (filter(it.second)) { + // we have a match + --n; + found.push_back(it.second); + // reset max iterations per cycle + iters = max_iters_per_cyle; + break; + } + } else // not there yet + ++i; + } + // we have enough + if (n == 0) break; + --iters; + // have we tried enough this cycle ? + if (!iters) { + // yes let's try the next cycle + --n; + iters = max_iters_per_cyle; + } + } + // visit the ones we found + size_t visited = 0; + for (const auto &ri: found) { + v(ri); + ++visited; + } + return visited; + } + + void NetDb::Load() { + // make sure we cleanup netDb from previous attempts + m_RouterInfos.clear(); + m_Floodfills.clear(); + + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + std::vector files; + m_Storage.Traverse(files); + for (const auto &path: files) + LoadRouterInfo(path, ts); + + LogPrint(eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size(), + " floodfils)"); + } + + void NetDb::SaveUpdated() { + int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0; + auto total = m_RouterInfos.size(); + auto totalFloodfills = m_Floodfills.size(); + uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT * 1000LL; + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + auto uptime = i2p::context.GetUptime(); + // routers don't expire if less than 90 or uptime is less than 1 hour + bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes + if (checkForExpiration && uptime > 3600) // 1 hour + expirationTimeout = i2p::context.IsFloodfill() ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT * 1000LL : + NETDB_MIN_EXPIRATION_TIMEOUT * 1000LL + + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT) * 1000LL * + NETDB_MIN_ROUTERS / total; + + auto own = i2p::context.GetSharedRouterInfo(); + for (auto &it: m_RouterInfos) { + if (it.second == own) continue; // skip own + std::string ident = it.second->GetIdentHashBase64(); + if (it.second->IsUpdated()) { + it.second->SaveToFile(m_Storage.Path(ident)); + it.second->SetUpdated(false); + it.second->SetUnreachable(false); + it.second->DeleteBuffer(); + updatedCount++; + continue; + } + // make router reachable back if too few routers or floodfills + if (it.second->IsUnreachable() && (total - deletedCount < NETDB_MIN_ROUTERS || + (it.second->IsFloodfill() && + totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) + it.second->SetUnreachable(false); + // find & mark expired routers + if (!it.second->IsReachable() && it.second->IsSSU(false)) { + if (ts > it.second->GetTimestamp() + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT * 1000LL) + // RouterInfo expires after 1 hour if uses introducer + it.second->SetUnreachable(true); + } else if (checkForExpiration && ts > it.second->GetTimestamp() + expirationTimeout) + it.second->SetUnreachable(true); + + if (it.second->IsUnreachable()) { + if (it.second->IsFloodfill()) deletedFloodfillsCount++; + // delete RI file + m_Storage.Remove(ident); + deletedCount++; + if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; + } + } // m_RouterInfos iteration + + m_RouterInfoBuffersPool.CleanUpMt(); + + if (updatedCount > 0) + LogPrint(eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers"); + if (deletedCount > 0) { + LogPrint(eLogInfo, "NetDb: Deleting ", deletedCount, " unreachable routers"); + // clean up RouterInfos table + { + std::unique_lock l(m_RouterInfosMutex); + for (auto it = m_RouterInfos.begin(); it != m_RouterInfos.end();) { + if (it->second->IsUnreachable()) { + if (m_PersistProfiles) it->second->SaveProfile(); + it = m_RouterInfos.erase(it); + continue; + } + ++it; + } + } + // clean up expired floodfills or not floodfills anymore + { + std::unique_lock l(m_FloodfillsMutex); + for (auto it = m_Floodfills.begin(); it != m_Floodfills.end();) + if ((*it)->IsUnreachable() || !(*it)->IsFloodfill()) + it = m_Floodfills.erase(it); + else + ++it; + } + } + } + + void + NetDb::RequestDestination(const IdentHash &destination, RequestedDestination::RequestComplete requestComplete, + bool direct) { + auto dest = m_Requests.CreateRequest(destination, false, requestComplete); // non-exploratory + if (!dest) { + LogPrint(eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); + return; + } + + auto floodfill = GetClosestFloodfill(destination, dest->GetExcludedPeers()); + if (floodfill) { + if (direct && !floodfill->IsReachableFrom(i2p::context.GetRouterInfo()) && + !i2p::transport::transports.IsConnected(floodfill->GetIdentHash())) + direct = false; // floodfill can't be reached directly + if (direct) + transports.SendMessage(floodfill->GetIdentHash(), + dest->CreateRequestMessage(floodfill->GetIdentHash())); + else { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = pool ? pool->GetNextOutboundTunnel(nullptr, + floodfill->GetCompatibleTransports(false)) + : nullptr; + auto inbound = pool ? pool->GetNextInboundTunnel(nullptr, floodfill->GetCompatibleTransports(true)) + : nullptr; + if (outbound && inbound) + outbound->SendTunnelDataMsg(floodfill->GetIdentHash(), 0, + dest->CreateRequestMessage(floodfill, inbound)); + else { + LogPrint(eLogError, "NetDb: ", destination.ToBase64(), + " destination requested, but no tunnels found"); + m_Requests.RequestComplete(destination, nullptr); + } + } + } else { + LogPrint(eLogError, "NetDb: ", destination.ToBase64(), + " destination requested, but no floodfills found"); + m_Requests.RequestComplete(destination, nullptr); + } + } + + void NetDb::RequestDestinationFrom(const IdentHash &destination, const IdentHash &from, bool exploritory, + RequestedDestination::RequestComplete requestComplete) { + + auto dest = m_Requests.CreateRequest(destination, exploritory, requestComplete); // non-exploratory + if (!dest) { + LogPrint(eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); + return; + } + LogPrint(eLogInfo, "NetDb: Destination ", destination.ToBase64(), " being requested directly from ", + from.ToBase64()); + // direct + transports.SendMessage(from, dest->CreateRequestMessage(nullptr, nullptr)); + } + + void NetDb::HandleNTCP2RouterInfoMsg(std::shared_ptr m) { + uint8_t flood = m->GetPayload()[0] & NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD; + bool updated; + auto ri = AddRouterInfo(m->GetPayload() + 1, m->GetPayloadLength() - 1, updated); // without flags + if (flood && updated && context.IsFloodfill() && ri) { + auto floodMsg = CreateDatabaseStoreMsg(ri, 0); // replyToken = 0 + Flood(ri->GetIdentHash(), floodMsg); + } + } + + void NetDb::HandleDatabaseStoreMsg(std::shared_ptr m) { + const uint8_t *buf = m->GetPayload(); + size_t len = m->GetSize(); + IdentHash ident(buf + DATABASE_STORE_KEY_OFFSET); + if (ident.IsZero()) { + LogPrint(eLogDebug, "NetDb: Database store with zero ident, dropped"); + return; + } + uint32_t replyToken = bufbe32toh(buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); + size_t offset = DATABASE_STORE_HEADER_SIZE; + if (replyToken) { + auto deliveryStatus = CreateDeliveryStatusMsg(replyToken); + uint32_t tunnelID = bufbe32toh(buf + offset); + offset += 4; + if (!tunnelID) // send response directly + transports.SendMessage(buf + offset, deliveryStatus); + else { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = pool ? pool->GetNextOutboundTunnel() : nullptr; + if (outbound) + outbound->SendTunnelDataMsg(buf + offset, tunnelID, deliveryStatus); + else + LogPrint(eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); + } + offset += 32; + } + // we must send reply back before this check + if (ident == i2p::context.GetIdentHash()) { + LogPrint(eLogDebug, "NetDb: Database store with own RouterInfo received, dropped"); + return; + } + size_t payloadOffset = offset; + + bool updated = false; + uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET]; + if (storeType) // LeaseSet or LeaseSet2 + { + if (!m->from) // unsolicited LS must be received directly + { + if (storeType == NETDB_STORE_TYPE_LEASESET) // 1 + { + LogPrint(eLogDebug, "NetDb: Store request: LeaseSet for ", ident.ToBase32()); + updated = AddLeaseSet(ident, buf + offset, len - offset); + } else // all others are considered as LeaseSet2 + { + LogPrint(eLogDebug, "NetDb: Store request: LeaseSet2 of type ", storeType, " for ", + ident.ToBase32()); + updated = AddLeaseSet2(ident, buf + offset, len - offset, storeType); + } + } + } else // RouterInfo + { + LogPrint(eLogDebug, "NetDb: Store request: RouterInfo"); + size_t size = bufbe16toh(buf + offset); + offset += 2; + if (size > MAX_RI_BUFFER_SIZE || size > len - offset) { + LogPrint(eLogError, "NetDb: Invalid RouterInfo length ", (int) size); + return; + } + uint8_t uncompressed[MAX_RI_BUFFER_SIZE]; + size_t uncompressedSize = m_Inflator.Inflate(buf + offset, size, uncompressed, MAX_RI_BUFFER_SIZE); + if (uncompressedSize && uncompressedSize < MAX_RI_BUFFER_SIZE) + updated = AddRouterInfo(ident, uncompressed, uncompressedSize); + else { + LogPrint(eLogInfo, "NetDb: Decompression failed ", uncompressedSize); + return; + } + } + + if (replyToken && context.IsFloodfill() && updated) { + // flood updated + auto floodMsg = NewI2NPShortMessage(); + uint8_t *payload = floodMsg->GetPayload(); + memcpy(payload, buf, 33); // key + type + htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token + size_t msgLen = len - payloadOffset; + floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen; + if (floodMsg->len < floodMsg->maxLen) { + memcpy(payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen); + floodMsg->FillI2NPMessageHeader(eI2NPDatabaseStore); + Flood(ident, floodMsg); + } else + LogPrint(eLogError, "NetDb: Database store message is too long ", floodMsg->len); + } + } + + void NetDb::HandleDatabaseSearchReplyMsg(std::shared_ptr msg) { + const uint8_t *buf = msg->GetPayload(); + char key[48]; + int l = i2p::data::ByteStreamToBase64(buf, 32, key, 48); + key[l] = 0; + int num = buf[32]; // num + LogPrint(eLogDebug, "NetDb: DatabaseSearchReply for ", key, " num=", num); + IdentHash ident(buf); + auto dest = m_Requests.FindRequest(ident); + if (dest) { + bool deleteDest = true; + if (num > 0) { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = pool ? pool->GetNextOutboundTunnel() : nullptr; + auto inbound = pool ? pool->GetNextInboundTunnel() : nullptr; + if (!dest->IsExploratory()) { + // reply to our destination. Try other floodfills + if (outbound && inbound) { + auto count = dest->GetExcludedPeers().size(); + if (count < 7) { + auto nextFloodfill = GetClosestFloodfill(dest->GetDestination(), + dest->GetExcludedPeers()); + if (nextFloodfill) { + // request destination + LogPrint(eLogDebug, "NetDb: Try ", key, " at ", count, " floodfill ", + nextFloodfill->GetIdentHash().ToBase64()); + outbound->SendTunnelDataMsg(nextFloodfill->GetIdentHash(), 0, + dest->CreateRequestMessage(nextFloodfill, inbound)); + deleteDest = false; + } + } else + LogPrint(eLogWarning, "NetDb: ", key, " was not found on ", count, " floodfills"); + } + } + + if (deleteDest) + // no more requests for the destinationation. delete it + m_Requests.RequestComplete(ident, nullptr); + } else + // no more requests for destination possible. delete it + m_Requests.RequestComplete(ident, nullptr); + } else if (!m_FloodfillBootstrap) + LogPrint(eLogWarning, "NetDb: Requested destination for ", key, " not found"); + + // try responses + for (int i = 0; i < num; i++) { + const uint8_t *router = buf + 33 + i * 32; + char peerHash[48]; + int l1 = i2p::data::ByteStreamToBase64(router, 32, peerHash, 48); + peerHash[l1] = 0; + LogPrint(eLogDebug, "NetDb: ", i, ": ", peerHash); + + auto r = FindRouter(router); + if (!r || i2p::util::GetMillisecondsSinceEpoch() > r->GetTimestamp() + 3600 * 1000LL) { + // router with ident not found or too old (1 hour) + LogPrint(eLogDebug, "NetDb: Found new/outdated router. Requesting RouterInfo..."); + if (m_FloodfillBootstrap) + RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true); + else + RequestDestination(router); + } else + LogPrint(eLogDebug, "NetDb: [:|||:]"); + } + } + + void NetDb::HandleDatabaseLookupMsg(std::shared_ptr msg) { + const uint8_t *buf = msg->GetPayload(); + IdentHash ident(buf); + if (ident.IsZero()) { + LogPrint(eLogError, "NetDb: DatabaseLookup for zero ident. Ignored"); + return; + } + char key[48]; + int l = i2p::data::ByteStreamToBase64(buf, 32, key, 48); + key[l] = 0; + + IdentHash replyIdent(buf + 32); + uint8_t flag = buf[64]; + + + LogPrint(eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int) flag); + uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; + const uint8_t *excluded = buf + 65; + uint32_t replyTunnelID = 0; + if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel + { + replyTunnelID = bufbe32toh(excluded); + excluded += 4; + } + uint16_t numExcluded = bufbe16toh(excluded); + excluded += 2; + if (numExcluded > 512 || (excluded - buf) + numExcluded * 32 > (int) msg->GetPayloadLength()) { + LogPrint(eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much"); + return; + } + + std::shared_ptr replyMsg; + if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) { + LogPrint(eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); + std::set excludedRouters; + const uint8_t *excluded_ident = excluded; + for (int i = 0; i < numExcluded; i++) { + excludedRouters.insert(excluded_ident); + excluded_ident += 32; + } + std::vector routers; + for (int i = 0; i < 3; i++) { + auto r = GetClosestNonFloodfill(ident, excludedRouters); + if (r) { + routers.push_back(r->GetIdentHash()); + excludedRouters.insert(r->GetIdentHash()); + } + } + replyMsg = CreateDatabaseSearchReply(ident, routers); + } else { + if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || + lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) { + auto router = FindRouter(ident); + if (router) { + LogPrint(eLogDebug, "NetDb: Requested RouterInfo ", key, " found"); + PopulateRouterInfoBuffer(router); + if (router->GetBuffer()) + replyMsg = CreateDatabaseStoreMsg(router); + } + } + + if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || + lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) { + auto leaseSet = FindLeaseSet(ident); + if (!leaseSet) { + // no lease set found + LogPrint(eLogDebug, "NetDb: Requested LeaseSet not found for ", ident.ToBase32()); + } else if (!leaseSet->IsExpired()) // we don't send back our LeaseSets + { + LogPrint(eLogDebug, "NetDb: Requested LeaseSet ", key, " found"); + replyMsg = CreateDatabaseStoreMsg(ident, leaseSet); + } + } + + if (!replyMsg) { + std::set excludedRouters; + const uint8_t *exclude_ident = excluded; + for (int i = 0; i < numExcluded; i++) { + excludedRouters.insert(exclude_ident); + exclude_ident += 32; + } + auto closestFloodfills = GetClosestFloodfills(ident, 3, excludedRouters, true); + if (closestFloodfills.empty()) + LogPrint(eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded"); + replyMsg = CreateDatabaseSearchReply(ident, closestFloodfills); + } + } + excluded += numExcluded * 32; + if (replyMsg) { + if (replyTunnelID) { + // encryption might be used though tunnel only + if (flag & + (DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_ECIES_FLAG)) // encrypted reply requested + { + const uint8_t *sessionKey = excluded; + const uint8_t numTags = excluded[32]; + if (numTags) { + if (flag & DATABASE_LOOKUP_ECIES_FLAG) { + uint64_t tag; + memcpy(&tag, excluded + 33, 8); + replyMsg = i2p::garlic::WrapECIESX25519Message(replyMsg, sessionKey, tag); + } else { + const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag + i2p::garlic::ElGamalAESSession garlic(sessionKey, sessionTag); + replyMsg = garlic.WrapSingleMessage(replyMsg); + } + if (!replyMsg) + LogPrint(eLogError, "NetDb: Failed to wrap message"); + } else + LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided"); + } + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel() : nullptr; + if (outbound) + outbound->SendTunnelDataMsg(replyIdent, replyTunnelID, replyMsg); + else + transports.SendMessage(replyIdent, i2p::CreateTunnelGatewayMsg(replyTunnelID, replyMsg)); + } else + transports.SendMessage(replyIdent, replyMsg); + } + } + + void NetDb::HandleDeliveryStatusMsg(std::shared_ptr msg) { + if (m_PublishReplyToken == bufbe32toh(msg->GetPayload() + DELIVERY_STATUS_MSGID_OFFSET)) { + LogPrint(eLogInfo, "NetDb: Publishing confirmed. reply token=", m_PublishReplyToken); + m_PublishExcluded.clear(); + m_PublishReplyToken = 0; + } + } + + void NetDb::Explore(int numDestinations) { + // new requests + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel() : nullptr; + auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel() : nullptr; + bool throughTunnels = outbound && inbound; + + uint8_t randomHash[32]; + std::vector msgs; + LogPrint(eLogInfo, "NetDb: Exploring new ", numDestinations, " routers ..."); + for (int i = 0; i < numDestinations; i++) { + RAND_bytes(randomHash, 32); + auto dest = m_Requests.CreateRequest(randomHash, true); // exploratory + if (!dest) { + LogPrint(eLogWarning, "NetDb: Exploratory destination is requested already"); + return; + } + auto floodfill = GetClosestFloodfill(randomHash, dest->GetExcludedPeers()); + if (floodfill) { + if (i2p::transport::transports.IsConnected(floodfill->GetIdentHash())) + throughTunnels = false; + if (throughTunnels) { + msgs.push_back(i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash(), 0, + CreateDatabaseStoreMsg() // tell floodfill about us + }); + msgs.push_back(i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash(), 0, + dest->CreateRequestMessage(floodfill, inbound) // explore + }); + } else + i2p::transport::transports.SendMessage(floodfill->GetIdentHash(), + dest->CreateRequestMessage(floodfill->GetIdentHash())); + } else + m_Requests.RequestComplete(randomHash, nullptr); + } + if (throughTunnels && msgs.size() > 0) + outbound->SendTunnelDataMsg(msgs); + } + + void NetDb::Publish() { + i2p::context.UpdateStats(); // for floodfill + + if (m_PublishExcluded.size() > NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS) { + LogPrint(eLogError, "NetDb: Couldn't publish our RouterInfo to ", NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS, + " closest routers. Try again"); + m_PublishExcluded.clear(); + } + + auto floodfill = GetClosestFloodfill(i2p::context.GetIdentHash(), m_PublishExcluded); + if (floodfill) { + uint32_t replyToken; + RAND_bytes((uint8_t * ) & replyToken, 4); + LogPrint(eLogInfo, "NetDb: Publishing our RouterInfo to ", + i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash()), ". reply token=", replyToken); + m_PublishExcluded.insert(floodfill->GetIdentHash()); + m_PublishReplyToken = replyToken; + if (floodfill->IsReachableFrom(i2p::context.GetRouterInfo()) || // are we able to connect? + i2p::transport::transports.IsConnected(floodfill->GetIdentHash())) // already connected ? + // send directly + transports.SendMessage(floodfill->GetIdentHash(), + CreateDatabaseStoreMsg(i2p::context.GetSharedRouterInfo(), replyToken)); + else { + // otherwise through exploratory + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel(nullptr, + floodfill->GetCompatibleTransports( + false)) : nullptr; + auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel(nullptr, + floodfill->GetCompatibleTransports( + true)) : nullptr; + if (inbound && outbound) + outbound->SendTunnelDataMsg(floodfill->GetIdentHash(), 0, + CreateDatabaseStoreMsg(i2p::context.GetSharedRouterInfo(), + replyToken, inbound)); + } + } + } + + void NetDb::Flood(const IdentHash &ident, std::shared_ptr floodMsg) { + std::set excluded; + excluded.insert(i2p::context.GetIdentHash()); // don't flood to itself + excluded.insert(ident); // don't flood back + for (int i = 0; i < 3; i++) { + auto floodfill = GetClosestFloodfill(ident, excluded); + if (floodfill) { + auto h = floodfill->GetIdentHash(); + LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64()); + transports.SendMessage(h, CopyI2NPMessage(floodMsg)); + excluded.insert(h); + } else + break; + } + } + + std::shared_ptr NetDb::GetRandomRouter() const { + return GetRandomRouter( + [](std::shared_ptr router) -> bool { + return !router->IsHidden(); + }); + } + + std::shared_ptr + NetDb::GetRandomRouter(std::shared_ptr compatibleWith, bool reverse) const { + return GetRandomRouter( + [compatibleWith, reverse](std::shared_ptr router) -> bool { + return !router->IsHidden() && router != compatibleWith && + (reverse ? compatibleWith->IsReachableFrom(*router) : + router->IsReachableFrom(*compatibleWith)) && + router->IsECIES(); + }); + } + + std::shared_ptr + NetDb::GetRandomPeerTestRouter(bool v4, const std::set &excluded) const { + return GetRandomRouter( + [v4, &excluded](std::shared_ptr router) -> bool { + return !router->IsHidden() && router->IsECIES() && + router->IsPeerTesting(v4) && !excluded.count(router->GetIdentHash()); + }); + } + + std::shared_ptr + NetDb::GetRandomSSU2PeerTestRouter(bool v4, const std::set &excluded) const { + return GetRandomRouter( + [v4, &excluded](std::shared_ptr router) -> bool { + return !router->IsHidden() && router->IsECIES() && + router->IsSSU2PeerTesting(v4) && !excluded.count(router->GetIdentHash()); + }); + } + + std::shared_ptr NetDb::GetRandomSSUV6Router() const { + return GetRandomRouter( + [](std::shared_ptr router) -> bool { + return !router->IsHidden() && router->IsECIES() && router->IsSSUV6(); + }); + } + + std::shared_ptr + NetDb::GetRandomIntroducer(bool v4, const std::set &excluded) const { + return GetRandomRouter( + [v4, &excluded](std::shared_ptr router) -> bool { + return !router->IsHidden() && router->IsECIES() && !router->IsFloodfill() && + // floodfills don't send relay tag + router->IsIntroducer(v4) && !excluded.count(router->GetIdentHash()); + }); + } + + std::shared_ptr + NetDb::GetRandomSSU2Introducer(bool v4, const std::set &excluded) const { + return GetRandomRouter( + [v4, &excluded](std::shared_ptr router) -> bool { + return !router->IsHidden() && router->IsSSU2Introducer(v4) && + !excluded.count(router->GetIdentHash()); + }); + } + + std::shared_ptr + NetDb::GetHighBandwidthRandomRouter(std::shared_ptr compatibleWith, bool reverse) const { + return GetRandomRouter( + [compatibleWith, reverse](std::shared_ptr router) -> bool { + return !router->IsHidden() && router != compatibleWith && + (reverse ? compatibleWith->IsReachableFrom(*router) : + router->IsReachableFrom(*compatibleWith)) && + (router->GetCaps() & RouterInfo::eHighBandwidth) && + router->GetVersion() >= NETDB_MIN_HIGHBANDWIDTH_VERSION && + router->IsECIES(); + }); + } + + template + std::shared_ptr NetDb::GetRandomRouter(Filter filter) const { + if (m_RouterInfos.empty()) + return 0; + uint16_t inds[3]; + RAND_bytes((uint8_t *) inds, sizeof(inds)); + std::unique_lock l(m_RouterInfosMutex); + inds[0] %= m_RouterInfos.size(); + auto it = m_RouterInfos.begin(); + std::advance(it, inds[0]); + // try random router + if (it != m_RouterInfos.end() && !it->second->IsUnreachable() && filter(it->second)) + return it->second; + // try some routers around + auto it1 = m_RouterInfos.begin(); + if (inds[0]) { + // before + inds[1] %= inds[0]; + std::advance(it1, (inds[1] + inds[0]) / 2); + } else + it1 = it; + auto it2 = it; + if (inds[0] < m_RouterInfos.size() - 1) { + // after + inds[2] %= (m_RouterInfos.size() - 1 - inds[0]); + inds[2] /= 2; + std::advance(it2, inds[2]); + } + // it1 - from, it2 - to + it = it1; + while (it != it2 && it != m_RouterInfos.end()) { + if (!it->second->IsUnreachable() && filter(it->second)) + return it->second; + it++; + } + // still not found, try from the beginning + it = m_RouterInfos.begin(); + while (it != it1 && it != m_RouterInfos.end()) { + if (!it->second->IsUnreachable() && filter(it->second)) + return it->second; + it++; + } + // still not found, try to the beginning + it = it2; + while (it != m_RouterInfos.end()) { + if (!it->second->IsUnreachable() && filter(it->second)) + return it->second; + it++; + } + return nullptr; // seems we have too few routers + } + + void NetDb::PostI2NPMsg(std::shared_ptr msg) { + if (msg) m_Queue.Put(msg); + } + + std::shared_ptr NetDb::GetClosestFloodfill(const IdentHash &destination, + const std::set &excluded, + bool closeThanUsOnly) const { + std::shared_ptr r; + XORMetric minMetric; + IdentHash destKey = CreateRoutingKey(destination); + if (closeThanUsOnly) + minMetric = destKey ^ i2p::context.GetIdentHash(); + else + minMetric.SetMax(); + std::unique_lock l(m_FloodfillsMutex); + for (const auto &it: m_Floodfills) { + if (!it->IsUnreachable()) { + XORMetric m = destKey ^ it->GetIdentHash(); + if (m < minMetric && !excluded.count(it->GetIdentHash())) { + minMetric = m; + r = it; + } + } + } + return r; + } + + std::vector NetDb::GetClosestFloodfills(const IdentHash &destination, size_t num, + std::set &excluded, bool closeThanUsOnly) const { + struct Sorted { + std::shared_ptr r; + XORMetric metric; + + bool operator<(const Sorted &other) const { return metric < other.metric; }; + }; + + std::set sorted; + IdentHash destKey = CreateRoutingKey(destination); + XORMetric ourMetric; + if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash(); + { + std::unique_lock l(m_FloodfillsMutex); + for (const auto &it: m_Floodfills) { + if (!it->IsUnreachable()) { + XORMetric m = destKey ^ it->GetIdentHash(); + if (closeThanUsOnly && ourMetric < m) continue; + if (sorted.size() < num) + sorted.insert({it, m}); + else if (m < sorted.rbegin()->metric) { + sorted.insert({it, m}); + sorted.erase(std::prev(sorted.end())); + } + } + } + } + + std::vector res; + size_t i = 0; + for (const auto &it: sorted) { + if (i < num) { + const auto &ident = it.r->GetIdentHash(); + if (!excluded.count(ident)) { + res.push_back(ident); + i++; + } + } else + break; + } + return res; + } + + std::shared_ptr NetDb::GetRandomRouterInFamily(FamilyID fam) const { + return GetRandomRouter( + [fam](std::shared_ptr router) -> bool { + return router->IsFamily(fam); + }); + } + + std::shared_ptr NetDb::GetClosestNonFloodfill(const IdentHash &destination, + const std::set &excluded) const { + std::shared_ptr r; + XORMetric minMetric; + IdentHash destKey = CreateRoutingKey(destination); + minMetric.SetMax(); + // must be called from NetDb thread only + for (const auto &it: m_RouterInfos) { + if (!it.second->IsFloodfill()) { + XORMetric m = destKey ^ it.first; + if (m < minMetric && !excluded.count(it.first)) { + minMetric = m; + r = it.second; + } + } + } + return r; + } + + void NetDb::ManageLeaseSets() { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + for (auto it = m_LeaseSets.begin(); it != m_LeaseSets.end();) { + if (!it->second->IsValid() || ts > it->second->GetExpirationTime() - LEASE_ENDDATE_THRESHOLD) { + LogPrint(eLogInfo, "NetDb: LeaseSet ", it->first.ToBase64(), " expired or invalid"); + it = m_LeaseSets.erase(it); + } else + ++it; + } + m_LeasesPool.CleanUpMt(); + } + + void NetDb::PopulateRouterInfoBuffer(std::shared_ptr r) { + if (!r || r->GetBuffer()) return; + r->LoadBuffer(m_Storage.Path(r->GetIdentHashBase64())); + } + } } diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index b14b84e7..79233555 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -32,162 +32,215 @@ #include "version.h" #include "util.h" -namespace i2p -{ -namespace data -{ - const int NETDB_MIN_ROUTERS = 90; - const int NETDB_MIN_FLOODFILLS = 5; - const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds - const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; - const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours - const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours - const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days - const int NETDB_PUBLISH_INTERVAL = 60 * 40; - const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds - const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; - const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 - const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38 - const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 +namespace i2p { + namespace data { + const int NETDB_MIN_ROUTERS = 90; + const int NETDB_MIN_FLOODFILLS = 5; + const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds + const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; + const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours + const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours + const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days + const int NETDB_PUBLISH_INTERVAL = 60 * 40; + const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds + const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; + const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 + const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 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 */ - typedef std::function)> LeaseSetVisitor; + /** function for visiting a leaseset stored in a floodfill */ + typedef std::function)> LeaseSetVisitor; - /** function for visiting a router info we have locally */ - typedef std::function)> RouterInfoVisitor; + /** function for visiting a router info we have locally */ + typedef std::function)> RouterInfoVisitor; - /** function for visiting a router info and determining if we want to use it */ - typedef std::function)> RouterInfoFilter; + /** function for visiting a router info and determining if we want to use it */ + typedef std::function)> RouterInfoFilter; - class NetDb - { - public: + class NetDb { + public: - NetDb (); - ~NetDb (); + NetDb(); - void Start (); - void Stop (); + ~NetDb(); - std::shared_ptr AddRouterInfo (const uint8_t * buf, int len); - 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 FindRouter (const IdentHash& ident) const; - std::shared_ptr FindLeaseSet (const IdentHash& destination) const; - std::shared_ptr FindRouterProfile (const IdentHash& ident) const; + void Start(); - void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true); - void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr); + void Stop(); - void HandleDatabaseStoreMsg (std::shared_ptr msg); - void HandleDatabaseSearchReplyMsg (std::shared_ptr msg); - void HandleDatabaseLookupMsg (std::shared_ptr msg); - void HandleNTCP2RouterInfoMsg (std::shared_ptr m); - void HandleDeliveryStatusMsg (std::shared_ptr msg); + std::shared_ptr AddRouterInfo(const uint8_t *buf, int len); - std::shared_ptr GetRandomRouter () const; - std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; - std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; - std::shared_ptr GetRandomPeerTestRouter (bool v4, const std::set& excluded) const; - std::shared_ptr GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const; - std::shared_ptr GetRandomSSUV6Router () const; // TODO: change to v6 peer test later - std::shared_ptr GetRandomIntroducer (bool v4, const std::set& excluded) const; - std::shared_ptr GetRandomSSU2Introducer (bool v4, const std::set& excluded) const; - std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded, bool closeThanUsOnly = false) const; - std::vector GetClosestFloodfills (const IdentHash& destination, size_t num, - std::set& excluded, bool closeThanUsOnly = false) const; - std::shared_ptr GetClosestNonFloodfill (const IdentHash& destination, const std::set& excluded) const; - std::shared_ptr GetRandomRouterInFamily (FamilyID fam) const; - void SetUnreachable (const IdentHash& ident, bool unreachable); + bool AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len); - void PostI2NPMsg (std::shared_ptr 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 */ - void SetHidden(bool hide); + bool AddLeaseSet2(const IdentHash &ident, const uint8_t *buf, int len, uint8_t storeType); - void Reseed (); - Families& GetFamilies () { return m_Families; }; + std::shared_ptr FindRouter(const IdentHash &ident) const; - // for web interface - int GetNumRouters () const { return m_RouterInfos.size (); }; - int GetNumFloodfills () const { return m_Floodfills.size (); }; - int GetNumLeaseSets () const { return m_LeaseSets.size (); }; + std::shared_ptr FindLeaseSet(const IdentHash &destination) const; - /** 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); + std::shared_ptr FindRouterProfile(const IdentHash &ident) const; - void ClearRouterInfos () { m_RouterInfos.clear (); }; - std::shared_ptr NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; - void PopulateRouterInfoBuffer (std::shared_ptr r); - std::shared_ptr NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); }; + void RequestDestination(const IdentHash &destination, + RequestedDestination::RequestComplete requestComplete = nullptr, + bool direct = true); - 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 msg); - 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 floodMsg); - void ManageLeaseSets (); - void ManageRequests (); + void HandleDatabaseSearchReplyMsg(std::shared_ptr msg); - void ReseedFromFloodfill(const RouterInfo & ri, int numRouters = 40, int numFloodfills = 20); + void HandleDatabaseLookupMsg(std::shared_ptr msg); - std::shared_ptr AddRouterInfo (const uint8_t * buf, int len, bool& updated); - std::shared_ptr AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated); + void HandleNTCP2RouterInfoMsg(std::shared_ptr m); - template - std::shared_ptr GetRandomRouter (Filter filter) const; + void HandleDeliveryStatusMsg(std::shared_ptr msg); - private: + std::shared_ptr GetRandomRouter() const; - mutable std::mutex m_LeaseSetsMutex; - std::unordered_map > m_LeaseSets; - mutable std::mutex m_RouterInfosMutex; - std::unordered_map > m_RouterInfos; - mutable std::mutex m_FloodfillsMutex; - std::list > m_Floodfills; + std::shared_ptr + GetRandomRouter(std::shared_ptr compatibleWith, bool reverse) const; - bool m_IsRunning; - std::thread * m_Thread; - i2p::util::Queue > m_Queue; // of I2NPDatabaseStoreMsg + std::shared_ptr + GetHighBandwidthRandomRouter(std::shared_ptr compatibleWith, bool reverse) const; - GzipInflator m_Inflator; - Reseeder * m_Reseeder; - Families m_Families; - i2p::fs::HashedStorage m_Storage; + std::shared_ptr + GetRandomPeerTestRouter(bool v4, const std::set &excluded) const; - friend class NetDbRequests; - NetDbRequests m_Requests; + std::shared_ptr + GetRandomSSU2PeerTestRouter(bool v4, const std::set &excluded) const; - bool m_PersistProfiles; + std::shared_ptr GetRandomSSUV6Router() const; // TODO: change to v6 peer test later + std::shared_ptr GetRandomIntroducer(bool v4, const std::set &excluded) const; - /** router info we are bootstrapping from or nullptr if we are not currently doing that*/ - std::shared_ptr m_FloodfillBootstrap; + std::shared_ptr + GetRandomSSU2Introducer(bool v4, const std::set &excluded) const; - /** true if in hidden mode */ - bool m_HiddenMode; + std::shared_ptr + GetClosestFloodfill(const IdentHash &destination, const std::set &excluded, + bool closeThanUsOnly = false) const; - std::set m_PublishExcluded; - uint32_t m_PublishReplyToken = 0; + std::vector GetClosestFloodfills(const IdentHash &destination, size_t num, + std::set &excluded, + bool closeThanUsOnly = false) const; - i2p::util::MemoryPoolMt m_RouterInfoBuffersPool; - i2p::util::MemoryPoolMt m_LeasesPool; - }; + std::shared_ptr + GetClosestNonFloodfill(const IdentHash &destination, const std::set &excluded) const; - extern NetDb netdb; -} + std::shared_ptr GetRandomRouterInFamily(FamilyID fam) const; + + void SetUnreachable(const IdentHash &ident, bool unreachable); + + void PostI2NPMsg(std::shared_ptr msg); + + /** set hidden mode, aka don't publish our RI to netdb and don't explore */ + void SetHidden(bool hide); + + 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 + NewRouterInfoBuffer() { return m_RouterInfoBuffersPool.AcquireSharedMt(); }; + + void PopulateRouterInfoBuffer(std::shared_ptr r); + + std::shared_ptr 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 floodMsg); + + void ManageLeaseSets(); + + void ManageRequests(); + + void ReseedFromFloodfill(const RouterInfo &ri, int numRouters = 40, int numFloodfills = 20); + + std::shared_ptr AddRouterInfo(const uint8_t *buf, int len, bool &updated); + + std::shared_ptr + AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len, bool &updated); + + template + std::shared_ptr GetRandomRouter(Filter filter) const; + + private: + + mutable std::mutex m_LeaseSetsMutex; + std::unordered_map > m_LeaseSets; + mutable std::mutex m_RouterInfosMutex; + std::unordered_map > m_RouterInfos; + mutable std::mutex m_FloodfillsMutex; + std::list > m_Floodfills; + + bool m_IsRunning; + std::thread *m_Thread; + i2p::util::Queue > 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 m_FloodfillBootstrap; + + /** true if in hidden mode */ + bool m_HiddenMode; + + std::set m_PublishExcluded; + uint32_t m_PublishReplyToken = 0; + + i2p::util::MemoryPoolMt m_RouterInfoBuffersPool; + i2p::util::MemoryPoolMt m_LeasesPool; + }; + + extern NetDb netdb; + } } #endif diff --git a/libi2pd/NetDbRequests.cpp b/libi2pd/NetDbRequests.cpp index e7aab34c..2240067e 100644 --- a/libi2pd/NetDbRequests.cpp +++ b/libi2pd/NetDbRequests.cpp @@ -12,157 +12,142 @@ #include "NetDb.hpp" #include "NetDbRequests.h" -namespace i2p -{ -namespace data -{ - std::shared_ptr RequestedDestination::CreateRequestMessage (std::shared_ptr router, - std::shared_ptr replyTunnel) - { - std::shared_ptr msg; - if(replyTunnel) - msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, - replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, - &m_ExcludedPeers); - else - msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); - if(router) - m_ExcludedPeers.insert (router->GetIdentHash ()); - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); - return msg; - } +namespace i2p { + namespace data { + std::shared_ptr + RequestedDestination::CreateRequestMessage(std::shared_ptr router, + std::shared_ptr replyTunnel) { + std::shared_ptr msg; + if (replyTunnel) + msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, + replyTunnel->GetNextIdentHash(), + replyTunnel->GetNextTunnelID(), m_IsExploratory, + &m_ExcludedPeers); + else + msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, + m_IsExploratory, &m_ExcludedPeers); + if (router) + m_ExcludedPeers.insert(router->GetIdentHash()); + m_CreationTime = i2p::util::GetSecondsSinceEpoch(); + return msg; + } - std::shared_ptr RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) - { - auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, - i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); - m_ExcludedPeers.insert (floodfill); - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); - return msg; - } + std::shared_ptr RequestedDestination::CreateRequestMessage(const IdentHash &floodfill) { + auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, + i2p::context.GetRouterInfo().GetIdentHash(), 0, false, + &m_ExcludedPeers); + m_ExcludedPeers.insert(floodfill); + m_CreationTime = i2p::util::GetSecondsSinceEpoch(); + return msg; + } - void RequestedDestination::ClearExcludedPeers () - { - m_ExcludedPeers.clear (); - } + void RequestedDestination::ClearExcludedPeers() { + m_ExcludedPeers.clear(); + } - void RequestedDestination::Success (std::shared_ptr r) - { - if (m_RequestComplete) - { - m_RequestComplete (r); - m_RequestComplete = nullptr; - } - } + void RequestedDestination::Success(std::shared_ptr r) { + if (m_RequestComplete) { + m_RequestComplete(r); + m_RequestComplete = nullptr; + } + } - void RequestedDestination::Fail () - { - if (m_RequestComplete) - { - m_RequestComplete (nullptr); - m_RequestComplete = nullptr; - } - } + void RequestedDestination::Fail() { + if (m_RequestComplete) { + m_RequestComplete(nullptr); + m_RequestComplete = nullptr; + } + } - void NetDbRequests::Start () - { - } + void NetDbRequests::Start() { + } - void NetDbRequests::Stop () - { - m_RequestedDestinations.clear (); - } + void NetDbRequests::Stop() { + m_RequestedDestinations.clear(); + } - std::shared_ptr NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete) - { - // request RouterInfo directly - auto dest = std::make_shared (destination, isExploratory); - dest->SetRequestComplete (requestComplete); - { - std::unique_lock l(m_RequestedDestinationsMutex); - if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted - return nullptr; - } - return dest; - } + std::shared_ptr + NetDbRequests::CreateRequest(const IdentHash &destination, bool isExploratory, + RequestedDestination::RequestComplete requestComplete) { + // request RouterInfo directly + auto dest = std::make_shared(destination, isExploratory); + dest->SetRequestComplete(requestComplete); + { + std::unique_lock l(m_RequestedDestinationsMutex); + if (!m_RequestedDestinations.insert(std::make_pair(destination, dest)).second) // not inserted + return nullptr; + } + return dest; + } - void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr r) - { - std::shared_ptr request; - { - std::unique_lock l(m_RequestedDestinationsMutex); - auto it = m_RequestedDestinations.find (ident); - if (it != m_RequestedDestinations.end ()) - { - request = it->second; - m_RequestedDestinations.erase (it); - } - } - if (request) - { - if (r) - request->Success (r); - else - request->Fail (); - } - } + void NetDbRequests::RequestComplete(const IdentHash &ident, std::shared_ptr r) { + std::shared_ptr request; + { + std::unique_lock l(m_RequestedDestinationsMutex); + auto it = m_RequestedDestinations.find(ident); + if (it != m_RequestedDestinations.end()) { + request = it->second; + m_RequestedDestinations.erase(it); + } + } + if (request) { + if (r) + request->Success(r); + else + request->Fail(); + } + } - std::shared_ptr NetDbRequests::FindRequest (const IdentHash& ident) const - { - std::unique_lock l(m_RequestedDestinationsMutex); - auto it = m_RequestedDestinations.find (ident); - if (it != m_RequestedDestinations.end ()) - return it->second; - return nullptr; - } + std::shared_ptr NetDbRequests::FindRequest(const IdentHash &ident) const { + std::unique_lock l(m_RequestedDestinationsMutex); + auto it = m_RequestedDestinations.find(ident); + if (it != m_RequestedDestinations.end()) + return it->second; + return nullptr; + } - void NetDbRequests::ManageRequests () - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - std::unique_lock l(m_RequestedDestinationsMutex); - for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) - { - auto& dest = it->second; - bool done = false; - if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute - { - if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds - { - auto count = dest->GetExcludedPeers ().size (); - if (!dest->IsExploratory () && count < 7) - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool->GetNextOutboundTunnel (); - auto inbound = pool->GetNextInboundTunnel (); - auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); - if (nextFloodfill && outbound && inbound) - outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (nextFloodfill, inbound)); - else - { - done = true; - if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels"); - if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels"); - if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills"); - } - } - else - { - if (!dest->IsExploratory ()) - LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); - done = true; - } - } - } - else // delete obsolete request - done = true; + void NetDbRequests::ManageRequests() { + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + std::unique_lock l(m_RequestedDestinationsMutex); + for (auto it = m_RequestedDestinations.begin(); it != m_RequestedDestinations.end();) { + auto &dest = it->second; + bool done = false; + if (ts < dest->GetCreationTime() + 60) // request is worthless after 1 minute + { + if (ts > dest->GetCreationTime() + 5) // no response for 5 seconds + { + auto count = dest->GetExcludedPeers().size(); + if (!dest->IsExploratory() && count < 7) { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool(); + auto outbound = pool->GetNextOutboundTunnel(); + auto inbound = pool->GetNextInboundTunnel(); + auto nextFloodfill = netdb.GetClosestFloodfill(dest->GetDestination(), + dest->GetExcludedPeers()); + if (nextFloodfill && outbound && inbound) + outbound->SendTunnelDataMsg(nextFloodfill->GetIdentHash(), 0, + dest->CreateRequestMessage(nextFloodfill, inbound)); + else { + done = true; + if (!inbound) LogPrint(eLogWarning, "NetDbReq: No inbound tunnels"); + if (!outbound) LogPrint(eLogWarning, "NetDbReq: No outbound tunnels"); + if (!nextFloodfill) LogPrint(eLogWarning, "NetDbReq: No more floodfills"); + } + } else { + if (!dest->IsExploratory()) + LogPrint(eLogWarning, "NetDbReq: ", dest->GetDestination().ToBase64(), + " not found after 7 attempts"); + done = true; + } + } + } else // delete obsolete request + done = true; - if (done) - it = m_RequestedDestinations.erase (it); - else - ++it; - } - } -} + if (done) + it = m_RequestedDestinations.erase(it); + else + ++it; + } + } + } } diff --git a/libi2pd/NetDbRequests.h b/libi2pd/NetDbRequests.h index cf2f0915..7cec6ed2 100644 --- a/libi2pd/NetDbRequests.h +++ b/libi2pd/NetDbRequests.h @@ -15,62 +15,76 @@ #include "Identity.h" #include "RouterInfo.h" -namespace i2p -{ -namespace data -{ - class RequestedDestination - { - public: +namespace i2p { + namespace data { + class RequestedDestination { + public: - typedef std::function)> RequestComplete; + typedef std::function)> RequestComplete; - RequestedDestination (const IdentHash& destination, bool isExploratory = false): - m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; - ~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); }; + RequestedDestination(const IdentHash &destination, bool isExploratory = false) : + m_Destination(destination), m_IsExploratory(isExploratory), m_CreationTime(0) {}; - const IdentHash& GetDestination () const { return m_Destination; }; - int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; - const std::set& 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 CreateRequestMessage (std::shared_ptr, std::shared_ptr replyTunnel); - std::shared_ptr CreateRequestMessage (const IdentHash& floodfill); + ~RequestedDestination() { if (m_RequestComplete) m_RequestComplete(nullptr); }; - void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; }; - bool IsRequestComplete () const { return m_RequestComplete != nullptr; }; - void Success (std::shared_ptr r); - void Fail (); + const IdentHash &GetDestination() const { return m_Destination; }; - private: + int GetNumExcludedPeers() const { return m_ExcludedPeers.size(); }; - IdentHash m_Destination; - bool m_IsExploratory; - std::set m_ExcludedPeers; - uint64_t m_CreationTime; - RequestComplete m_RequestComplete; - }; + const std::set &GetExcludedPeers() { return m_ExcludedPeers; }; - class NetDbRequests - { - public: + void ClearExcludedPeers(); - void Start (); - void Stop (); + bool IsExploratory() const { return m_IsExploratory; }; - std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); - void RequestComplete (const IdentHash& ident, std::shared_ptr r); - std::shared_ptr FindRequest (const IdentHash& ident) const; - void ManageRequests (); + bool IsExcluded(const IdentHash &ident) const { return m_ExcludedPeers.count(ident); }; - private: + uint64_t GetCreationTime() const { return m_CreationTime; }; - mutable std::mutex m_RequestedDestinationsMutex; - std::map > m_RequestedDestinations; - }; -} + std::shared_ptr CreateRequestMessage(std::shared_ptr, + std::shared_ptr replyTunnel); + + std::shared_ptr CreateRequestMessage(const IdentHash &floodfill); + + void SetRequestComplete(const RequestComplete &requestComplete) { m_RequestComplete = requestComplete; }; + + bool IsRequestComplete() const { return m_RequestComplete != nullptr; }; + + void Success(std::shared_ptr r); + + void Fail(); + + private: + + IdentHash m_Destination; + bool m_IsExploratory; + std::set m_ExcludedPeers; + uint64_t m_CreationTime; + RequestComplete m_RequestComplete; + }; + + class NetDbRequests { + public: + + void Start(); + + void Stop(); + + std::shared_ptr CreateRequest(const IdentHash &destination, bool isExploratory, + RequestedDestination::RequestComplete requestComplete = nullptr); + + void RequestComplete(const IdentHash &ident, std::shared_ptr r); + + std::shared_ptr FindRequest(const IdentHash &ident) const; + + void ManageRequests(); + + private: + + mutable std::mutex m_RequestedDestinationsMutex; + std::map > m_RequestedDestinations; + }; + } } #endif diff --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp index 20b3ab2a..e2a81d57 100644 --- a/libi2pd/Poly1305.cpp +++ b/libi2pd/Poly1305.cpp @@ -9,17 +9,14 @@ #include "Poly1305.h" #if !OPENSSL_AEAD_CHACHA20_POLY1305 -namespace i2p -{ -namespace crypto -{ - void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz) - { - Poly1305 p(key); - p.Update(buf, sz); - p.Finish(out); - } -} +namespace i2p { + namespace crypto { + void Poly1305HMAC(uint64_t *out, const uint64_t *key, const uint8_t *buf, std::size_t sz) { + Poly1305 p(key); + p.Update(buf, sz); + p.Finish(out); + } + } } #endif diff --git a/libi2pd/Poly1305.h b/libi2pd/Poly1305.h index db659b84..a392edab 100644 --- a/libi2pd/Poly1305.h +++ b/libi2pd/Poly1305.h @@ -8,253 +8,229 @@ #ifndef LIBI2PD_POLY1305_H #define LIBI2PD_POLY1305_H + #include #include #include "Crypto.h" #if !OPENSSL_AEAD_CHACHA20_POLY1305 -namespace i2p -{ -namespace crypto -{ - const std::size_t POLY1305_DIGEST_BYTES = 16; - const std::size_t POLY1305_DIGEST_DWORDS = 4; - const std::size_t POLY1305_KEY_BYTES = 32; - const std::size_t POLY1305_KEY_DWORDS = 8; - const std::size_t POLY1305_BLOCK_BYTES = 16; +namespace i2p { + namespace crypto { + const std::size_t POLY1305_DIGEST_BYTES = 16; + const std::size_t POLY1305_DIGEST_DWORDS = 4; + const std::size_t POLY1305_KEY_BYTES = 32; + const std::size_t POLY1305_KEY_DWORDS = 8; + const std::size_t POLY1305_BLOCK_BYTES = 16; - namespace poly1305 - { - struct LongBlock - { - unsigned long data[17]; - operator unsigned long * () - { - return data; - } - }; + namespace poly1305 { + struct LongBlock { + unsigned long data[17]; - struct Block - { - unsigned char data[17]; + operator unsigned long *() { + return data; + } + }; - void Zero() - { - memset(data, 0, sizeof(data)); - } + struct Block { + unsigned char data[17]; - operator uint8_t * () - { - return data; - } + void Zero() { + memset(data, 0, sizeof(data)); + } - Block & operator += (const Block & other) - { - 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; - } + operator uint8_t *() { + return data; + } - Block & operator %=(const LongBlock & other) - { - unsigned long u; - unsigned int i; - 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+=(const Block &other) { + 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 Block & other) - { - memcpy(data, other.data, sizeof(data)); - return *this; - } + Block &operator%=(const LongBlock &other) { + unsigned long u; + unsigned int i; + 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 ~ () - { - 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]); + Block &operator=(const Block &other) { + memcpy(data, other.data, sizeof(data)); + return *this; + } - 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) - { - 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; - } + return *this; + } - template - void Put(const Int_t * d, uint8_t last=0) - { - memcpy(data, d, 16); - data[16] = last; - } - }; + void PutKey(const uint64_t *key_l) { + 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; + } - struct Buffer - { - uint8_t data[POLY1305_BLOCK_BYTES]; + template + void Put(const Int_t *d, uint8_t last = 0) { + memcpy(data, d, 16); + data[16] = last; + } + }; - operator uint8_t * () - { - return data; - } - }; - } + struct Buffer { + uint8_t data[POLY1305_BLOCK_BYTES]; - struct Poly1305 - { - Poly1305(const uint64_t * key) - { - m_Leftover = 0; - m_H.Zero(); - m_Final = 0; - m_R.PutKey(key); - m_Pad.Put(key + 2); - } + operator uint8_t *() { + return data; + } + }; + } - void Update(const uint8_t * buf, size_t sz) - { - // process leftover - if(m_Leftover) - { - size_t want = POLY1305_BLOCK_BYTES - m_Leftover; - if(want > sz) want = sz; - 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; - } - } + struct Poly1305 { + Poly1305(const uint64_t *key) { + m_Leftover = 0; + m_H.Zero(); + m_Final = 0; + m_R.PutKey(key); + m_Pad.Put(key + 2); + } - void Blocks(const uint8_t * buf, size_t sz) - { - const unsigned char hi = m_Final ^ 1; - while (sz >= POLY1305_BLOCK_BYTES) { - unsigned long u; - unsigned int i, j; - m_Msg.Put(buf, hi); - /* h += m */ - m_H += m_Msg; + void Update(const uint8_t *buf, size_t sz) { + // process leftover + if (m_Leftover) { + size_t want = POLY1305_BLOCK_BYTES - m_Leftover; + if (want > sz) want = sz; + 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; + } + } - /* h *= r */ - for (i = 0; i < 17; i++) { - u = 0; - for (j = 0; j <= i ; j++) { - u += (unsigned short)m_H.data[j] * m_R.data[i - j]; - } - for (j = i + 1; j < 17; j++) { - unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j]; - 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 Blocks(const uint8_t *buf, size_t sz) { + const unsigned char hi = m_Final ^ 1; + while (sz >= POLY1305_BLOCK_BYTES) { + unsigned long u; + unsigned int i, j; + m_Msg.Put(buf, hi); + /* h += m */ + m_H += m_Msg; - void Finish(uint64_t * out) - { - // process leftovers - if(m_Leftover) - { - size_t idx = m_Leftover; - m_Buffer[idx++] = 1; - for(; idx < POLY1305_BLOCK_BYTES; idx++) - m_Buffer[idx] = 0; - m_Final = 1; - Blocks(m_Buffer, POLY1305_BLOCK_BYTES); - } + /* h *= r */ + for (i = 0; i < 17; i++) { + u = 0; + for (j = 0; j <= i; j++) { + u += (unsigned short) m_H.data[j] * m_R.data[i - j]; + } + for (j = i + 1; j < 17; j++) { + unsigned long v = (unsigned short) m_H.data[j] * m_R.data[i + 17 - j]; + 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; + } + } - // freeze H - ~m_H; - // add pad - m_H += m_Pad; - // copy digest - memcpy(out, m_H, 16); - } + void Finish(uint64_t *out) { + // process leftovers + if (m_Leftover) { + size_t idx = m_Leftover; + m_Buffer[idx++] = 1; + for (; idx < POLY1305_BLOCK_BYTES; idx++) + m_Buffer[idx] = 0; + m_Final = 1; + Blocks(m_Buffer, POLY1305_BLOCK_BYTES); + } - 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; - }; + // freeze H + ~m_H; + // add pad + m_H += m_Pad; + // copy digest + memcpy(out, m_H, 16); + } - 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 diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index 55b95831..ec15c558 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -14,190 +14,165 @@ #include "Log.h" #include "Profiling.h" -namespace i2p -{ -namespace data -{ - i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt"); +namespace i2p { + namespace data { + i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt"); - RouterProfile::RouterProfile (): - m_LastUpdateTime (boost::posix_time::second_clock::local_time()), - m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), - m_NumTimesTaken (0), m_NumTimesRejected (0) - { - } + RouterProfile::RouterProfile() : + m_LastUpdateTime(boost::posix_time::second_clock::local_time()), + m_NumTunnelsAgreed(0), m_NumTunnelsDeclined(0), m_NumTunnelsNonReplied(0), + m_NumTimesTaken(0), m_NumTimesRejected(0) { + } - boost::posix_time::ptime RouterProfile::GetTime () const - { - return boost::posix_time::second_clock::local_time(); - } + boost::posix_time::ptime RouterProfile::GetTime() const { + return boost::posix_time::second_clock::local_time(); + } - void RouterProfile::UpdateTime () - { - m_LastUpdateTime = GetTime (); - } + void RouterProfile::UpdateTime() { + m_LastUpdateTime = GetTime(); + } - void RouterProfile::Save (const IdentHash& identHash) - { - // fill sections - boost::property_tree::ptree participation; - participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed); - participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined); - participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied); - boost::property_tree::ptree usage; - usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); - usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); - // fill property tree - boost::property_tree::ptree pt; - 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_USAGE, usage); + void RouterProfile::Save(const IdentHash &identHash) { + // fill sections + boost::property_tree::ptree participation; + participation.put(PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed); + participation.put(PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined); + participation.put(PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied); + boost::property_tree::ptree usage; + usage.put(PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); + usage.put(PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); + // fill property tree + boost::property_tree::ptree pt; + 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_USAGE, usage); - // save to file - std::string ident = identHash.ToBase64 (); - std::string path = m_ProfilesStorage.Path(ident); + // save to file + std::string ident = identHash.ToBase64(); + std::string path = m_ProfilesStorage.Path(ident); - try { - boost::property_tree::write_ini (path, pt); - } catch (std::exception& ex) { - /* boost exception verbose enough */ - LogPrint (eLogError, "Profiling: ", ex.what ()); - } - } + try { + boost::property_tree::write_ini(path, pt); + } catch (std::exception &ex) { + /* boost exception verbose enough */ + LogPrint(eLogError, "Profiling: ", ex.what()); + } + } - void RouterProfile::Load (const IdentHash& identHash) - { - std::string ident = identHash.ToBase64 (); - std::string path = m_ProfilesStorage.Path(ident); - boost::property_tree::ptree pt; + void RouterProfile::Load(const IdentHash &identHash) { + std::string ident = identHash.ToBase64(); + std::string path = m_ProfilesStorage.Path(ident); + boost::property_tree::ptree pt; - if (!i2p::fs::Exists(path)) - { - LogPrint(eLogWarning, "Profiling: No profile yet for ", ident); - return; - } + if (!i2p::fs::Exists(path)) { + LogPrint(eLogWarning, "Profiling: No profile yet for ", ident); + return; + } - try - { - boost::property_tree::read_ini (path, pt); - } catch (std::exception& ex) - { - /* boost exception verbose enough */ - LogPrint (eLogError, "Profiling: ", ex.what ()); - return; - } + try { + boost::property_tree::read_ini(path, pt); + } catch (std::exception &ex) { + /* boost exception verbose enough */ + LogPrint(eLogError, "Profiling: ", ex.what()); + return; + } - try - { - auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); - if (t.length () > 0) - m_LastUpdateTime = boost::posix_time::time_from_string (t); - if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) - { - try - { - // read participations - auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); - m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); - m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); - m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); - } - catch (boost::property_tree::ptree_bad_path& ex) - { - LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident); - } - try - { - // read usage - auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); - m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); - m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); - } - catch (boost::property_tree::ptree_bad_path& ex) - { - LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident); - } - } - else - *this = RouterProfile (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ()); - } - } + try { + auto t = pt.get(PEER_PROFILE_LAST_UPDATE_TIME, ""); + if (t.length() > 0) + m_LastUpdateTime = boost::posix_time::time_from_string(t); + if ((GetTime() - m_LastUpdateTime).hours() < PEER_PROFILE_EXPIRATION_TIMEOUT) { + try { + // read participations + auto participations = pt.get_child(PEER_PROFILE_SECTION_PARTICIPATION); + m_NumTunnelsAgreed = participations.get(PEER_PROFILE_PARTICIPATION_AGREED, 0); + m_NumTunnelsDeclined = participations.get(PEER_PROFILE_PARTICIPATION_DECLINED, 0); + m_NumTunnelsNonReplied = participations.get(PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); + } + catch (boost::property_tree::ptree_bad_path &ex) { + LogPrint(eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, + " in profile for ", ident); + } + try { + // read usage + auto usage = pt.get_child(PEER_PROFILE_SECTION_USAGE); + m_NumTimesTaken = usage.get(PEER_PROFILE_USAGE_TAKEN, 0); + m_NumTimesRejected = usage.get(PEER_PROFILE_USAGE_REJECTED, 0); + } + catch (boost::property_tree::ptree_bad_path &ex) { + LogPrint(eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE, + " in profile for ", ident); + } + } else + *this = RouterProfile(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "Profiling: Can't read profile ", ident, " :", ex.what()); + } + } - void RouterProfile::TunnelBuildResponse (uint8_t ret) - { - UpdateTime (); - if (ret > 0) - m_NumTunnelsDeclined++; - else - m_NumTunnelsAgreed++; - } + void RouterProfile::TunnelBuildResponse(uint8_t ret) { + UpdateTime(); + if (ret > 0) + m_NumTunnelsDeclined++; + else + m_NumTunnelsAgreed++; + } - void RouterProfile::TunnelNonReplied () - { - m_NumTunnelsNonReplied++; - UpdateTime (); - } + void RouterProfile::TunnelNonReplied() { + m_NumTunnelsNonReplied++; + UpdateTime(); + } - bool RouterProfile::IsLowPartcipationRate () const - { - return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate - } + bool RouterProfile::IsLowPartcipationRate() const { + return 4 * m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate + } - bool RouterProfile::IsLowReplyRate () const - { - auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined; - return m_NumTunnelsNonReplied > 10*(total + 1); - } + bool RouterProfile::IsLowReplyRate() const { + auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined; + return m_NumTunnelsNonReplied > 10 * (total + 1); + } - bool RouterProfile::IsBad () - { - auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; - if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) - { - // reset profile - m_NumTunnelsAgreed = 0; - m_NumTunnelsDeclined = 0; - m_NumTunnelsNonReplied = 0; - isBad = false; - } - if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++; - return isBad; - } + bool RouterProfile::IsBad() { + auto isBad = IsAlwaysDeclining() || IsLowPartcipationRate() /*|| IsLowReplyRate ()*/; + if (isBad && m_NumTimesRejected > 10 * (m_NumTimesTaken + 1)) { + // reset profile + m_NumTunnelsAgreed = 0; + m_NumTunnelsDeclined = 0; + m_NumTunnelsNonReplied = 0; + isBad = false; + } + if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++; + return isBad; + } - std::shared_ptr GetRouterProfile (const IdentHash& identHash) - { - auto profile = std::make_shared (); - profile->Load (identHash); // if possible - return profile; - } + std::shared_ptr GetRouterProfile(const IdentHash &identHash) { + auto profile = std::make_shared(); + profile->Load(identHash); // if possible + return profile; + } - void InitProfilesStorage () - { - m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); - m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); - } + void InitProfilesStorage() { + m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); + m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); + } - void DeleteObsoleteProfiles () - { - struct stat st; - std::time_t now = std::time(nullptr); + void DeleteObsoleteProfiles() { + struct stat st; + std::time_t now = std::time(nullptr); - std::vector files; - m_ProfilesStorage.Traverse(files); - for (const auto& path: files) { - if (stat(path.c_str(), &st) != 0) { - LogPrint(eLogWarning, "Profiling: Can't stat(): ", path); - continue; - } - if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) { - LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path); - i2p::fs::Remove(path); - } - } - } -} + std::vector files; + m_ProfilesStorage.Traverse(files); + for (const auto &path: files) { + if (stat(path.c_str(), &st) != 0) { + LogPrint(eLogWarning, "Profiling: Can't stat(): ", path); + continue; + } + if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) { + LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path); + i2p::fs::Remove(path); + } + } + } + } } diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 49e362ca..9ff1fe52 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -13,65 +13,70 @@ #include #include "Identity.h" -namespace i2p -{ -namespace data -{ - // sections - const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; - const char PEER_PROFILE_SECTION_USAGE[] = "usage"; - // params - const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; - const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed"; - const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined"; - const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied"; - const char PEER_PROFILE_USAGE_TAKEN[] = "taken"; - const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; +namespace i2p { + namespace data { + // sections + const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; + const char PEER_PROFILE_SECTION_USAGE[] = "usage"; + // params + const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; + const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed"; + const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined"; + const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied"; + 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_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day) - const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours) + 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_VARIANCE = 3 * 3600; // in seconds (3 hours) - class RouterProfile - { - public: + class RouterProfile { + public: - RouterProfile (); - RouterProfile& operator= (const RouterProfile& ) = default; + RouterProfile(); - void Save (const IdentHash& identHash); - void Load (const IdentHash& identHash); + RouterProfile &operator=(const RouterProfile &) = default; - bool IsBad (); + void Save(const IdentHash &identHash); - void TunnelBuildResponse (uint8_t ret); - void TunnelNonReplied (); + void Load(const IdentHash &identHash); - private: + bool IsBad(); - boost::posix_time::ptime GetTime () const; - void UpdateTime (); + void TunnelBuildResponse(uint8_t ret); - bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; - bool IsLowPartcipationRate () const; - bool IsLowReplyRate () const; + void TunnelNonReplied(); - private: + 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; - }; + boost::posix_time::ptime GetTime() const; - std::shared_ptr GetRouterProfile (const IdentHash& identHash); - void InitProfilesStorage (); - void DeleteObsoleteProfiles (); -} + void UpdateTime(); + + 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 GetRouterProfile(const IdentHash &identHash); + + void InitProfilesStorage(); + + void DeleteObsoleteProfiles(); + } } #endif diff --git a/libi2pd/Queue.h b/libi2pd/Queue.h index 441f8c3a..5a823c42 100644 --- a/libi2pd/Queue.h +++ b/libi2pd/Queue.h @@ -17,117 +17,100 @@ #include #include -namespace i2p -{ -namespace util -{ - template - class Queue - { - public: +namespace i2p { + namespace util { + template + class Queue { + public: - void Put (Element e) - { - std::unique_lock l(m_QueueMutex); - m_Queue.push (std::move(e)); - m_NonEmpty.notify_one (); - } + void Put(Element e) { + std::unique_lock l(m_QueueMutex); + m_Queue.push(std::move(e)); + m_NonEmpty.notify_one(); + } - templateclass Container, typename... R> - void Put (const Container& vec) - { - if (!vec.empty ()) - { - std::unique_lock l(m_QueueMutex); - for (const auto& it: vec) - m_Queue.push (std::move(it)); - m_NonEmpty.notify_one (); - } - } + template class Container, typename... R> + void Put(const Container &vec) { + if (!vec.empty()) { + std::unique_lock l(m_QueueMutex); + for (const auto &it: vec) + m_Queue.push(std::move(it)); + m_NonEmpty.notify_one(); + } + } - Element GetNext () - { - std::unique_lock l(m_QueueMutex); - auto el = GetNonThreadSafe (); - if (!el) - { - m_NonEmpty.wait (l); - el = GetNonThreadSafe (); - } - return el; - } + Element GetNext() { + std::unique_lock l(m_QueueMutex); + auto el = GetNonThreadSafe(); + if (!el) { + m_NonEmpty.wait(l); + el = GetNonThreadSafe(); + } + return el; + } - Element GetNextWithTimeout (int usec) - { - std::unique_lock l(m_QueueMutex); - auto el = GetNonThreadSafe (); - if (!el) - { - m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec)); - el = GetNonThreadSafe (); - } - return el; - } + Element GetNextWithTimeout(int usec) { + std::unique_lock l(m_QueueMutex); + auto el = GetNonThreadSafe(); + if (!el) { + m_NonEmpty.wait_for(l, std::chrono::milliseconds(usec)); + el = GetNonThreadSafe(); + } + return el; + } - void Wait () - { - std::unique_lock l(m_QueueMutex); - m_NonEmpty.wait (l); - } + void Wait() { + std::unique_lock l(m_QueueMutex); + m_NonEmpty.wait(l); + } - bool Wait (int sec, int usec) - { - std::unique_lock l(m_QueueMutex); - return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout; - } + bool Wait(int sec, int usec) { + std::unique_lock l(m_QueueMutex); + return m_NonEmpty.wait_for(l, std::chrono::seconds(sec) + std::chrono::milliseconds(usec)) != + std::cv_status::timeout; + } - bool IsEmpty () - { - std::unique_lock l(m_QueueMutex); - return m_Queue.empty (); - } + bool IsEmpty() { + std::unique_lock l(m_QueueMutex); + return m_Queue.empty(); + } - int GetSize () - { - std::unique_lock l(m_QueueMutex); - return m_Queue.size (); - } + int GetSize() { + std::unique_lock l(m_QueueMutex); + return m_Queue.size(); + } - void WakeUp () { m_NonEmpty.notify_all (); }; + void WakeUp() { m_NonEmpty.notify_all(); }; - Element Get () - { - std::unique_lock l(m_QueueMutex); - return GetNonThreadSafe (); - } + Element Get() { + std::unique_lock l(m_QueueMutex); + return GetNonThreadSafe(); + } - Element Peek () - { - std::unique_lock l(m_QueueMutex); - return GetNonThreadSafe (true); - } + Element Peek() { + std::unique_lock l(m_QueueMutex); + return GetNonThreadSafe(true); + } - private: + private: - Element GetNonThreadSafe (bool peek = false) - { - if (!m_Queue.empty ()) - { - auto el = m_Queue.front (); - if (!peek) - m_Queue.pop (); - return el; - } - return nullptr; - } + Element GetNonThreadSafe(bool peek = false) { + if (!m_Queue.empty()) { + auto el = m_Queue.front(); + if (!peek) + m_Queue.pop(); + return el; + } + return nullptr; + } - private: + private: - std::queue m_Queue; - std::mutex m_QueueMutex; - std::condition_variable m_NonEmpty; - }; -} + std::queue m_Queue; + std::mutex m_QueueMutex; + std::condition_variable m_NonEmpty; + }; + } } #endif diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 4c23b4cc..27827e8a 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -27,772 +27,702 @@ #include "util.h" #include "Config.h" -namespace i2p -{ -namespace data -{ +namespace i2p { + namespace data { - Reseeder::Reseeder() - { - } + Reseeder::Reseeder() { + } - Reseeder::~Reseeder() - { - } + Reseeder::~Reseeder() { + } - /** - @brief tries to bootstrap into I2P network (from local files and servers, with respect of options) - */ - void Reseeder::Bootstrap () - { - std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); - std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); + /** + @brief tries to bootstrap into I2P network (from local files and servers, with respect of options) + */ + void Reseeder::Bootstrap() { + std::string su3FileName; + i2p::config::GetOption("reseed.file", su3FileName); + std::string zipFileName; + i2p::config::GetOption("reseed.zipfile", zipFileName); - if (su3FileName.length() > 0) // bootstrap from SU3 file or URL - { - int num; - if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") - { - num = ReseedFromSU3Url (su3FileName); // from https URL - } - else - { - num = ProcessSU3File (su3FileName.c_str ()); - } - if (num == 0) - LogPrint (eLogWarning, "Reseed: Failed to reseed from ", su3FileName); - } - else if (zipFileName.length() > 0) // bootstrap from ZIP file - { - int num = ProcessZIPFile (zipFileName.c_str ()); - if (num == 0) - LogPrint (eLogWarning, "Reseed: Failed to reseed from ", zipFileName); - } - else // bootstrap from reseed servers - { - int num = ReseedFromServers (); - if (num == 0) - LogPrint (eLogWarning, "Reseed: Failed to reseed from servers"); - } - } + if (su3FileName.length() > 0) // bootstrap from SU3 file or URL + { + int num; + if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") { + num = ReseedFromSU3Url(su3FileName); // from https URL + } else { + num = ProcessSU3File(su3FileName.c_str()); + } + if (num == 0) + LogPrint(eLogWarning, "Reseed: Failed to reseed from ", su3FileName); + } else if (zipFileName.length() > 0) // bootstrap from ZIP file + { + int num = ProcessZIPFile(zipFileName.c_str()); + if (num == 0) + LogPrint(eLogWarning, "Reseed: Failed to reseed from ", zipFileName); + } else // bootstrap from reseed servers + { + int num = ReseedFromServers(); + if (num == 0) + LogPrint(eLogWarning, "Reseed: Failed to reseed from servers"); + } + } - /** - * @brief bootstrap from random server, retry 10 times - * @return number of entries added to netDb - */ - int Reseeder::ReseedFromServers () - { - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool yggdrasil; i2p::config::GetOption("meshnets.yggdrasil", yggdrasil); + /** + * @brief bootstrap from random server, retry 10 times + * @return number of entries added to netDb + */ + int Reseeder::ReseedFromServers() { + bool ipv6; + i2p::config::GetOption("ipv6", ipv6); + bool ipv4; + i2p::config::GetOption("ipv4", ipv4); + bool yggdrasil; + i2p::config::GetOption("meshnets.yggdrasil", yggdrasil); - std::vector httpsReseedHostList; - if (ipv4 || ipv6) - { - std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs); - if (!reseedURLs.empty ()) - boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); - } + std::vector httpsReseedHostList; + if (ipv4 || ipv6) { + std::string reseedURLs; + i2p::config::GetOption("reseed.urls", reseedURLs); + if (!reseedURLs.empty()) + boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); + } - std::vector yggReseedHostList; - if (yggdrasil && !i2p::util::net::GetYggdrasilAddress ().is_unspecified ()) - { - LogPrint (eLogInfo, "Reseed: Yggdrasil is supported"); - std::string yggReseedURLs; i2p::config::GetOption("reseed.yggurls", yggReseedURLs); - if (!yggReseedURLs.empty ()) - boost::split(yggReseedHostList, yggReseedURLs, boost::is_any_of(","), boost::token_compress_on); - } + std::vector yggReseedHostList; + if (yggdrasil && !i2p::util::net::GetYggdrasilAddress().is_unspecified()) { + LogPrint(eLogInfo, "Reseed: Yggdrasil is supported"); + std::string yggReseedURLs; + i2p::config::GetOption("reseed.yggurls", yggReseedURLs); + if (!yggReseedURLs.empty()) + boost::split(yggReseedHostList, yggReseedURLs, boost::is_any_of(","), boost::token_compress_on); + } - if (httpsReseedHostList.empty () && yggReseedHostList.empty()) - { - LogPrint (eLogWarning, "Reseed: No reseed servers specified"); - return 0; - } + if (httpsReseedHostList.empty() && yggReseedHostList.empty()) { + LogPrint(eLogWarning, "Reseed: No reseed servers specified"); + return 0; + } - int reseedRetries = 0; - while (reseedRetries < 10) - { - auto ind = rand () % (httpsReseedHostList.size () + yggReseedHostList.size ()); - bool isHttps = ind < httpsReseedHostList.size (); - std::string reseedUrl = isHttps ? httpsReseedHostList[ind] : - yggReseedHostList[ind - httpsReseedHostList.size ()]; - reseedUrl += "i2pseeds.su3"; - auto num = ReseedFromSU3Url (reseedUrl, isHttps); - if (num > 0) return num; // success - reseedRetries++; - } - LogPrint (eLogWarning, "Reseed: Failed to reseed from servers after 10 attempts"); - return 0; - } + int reseedRetries = 0; + while (reseedRetries < 10) { + auto ind = rand() % (httpsReseedHostList.size() + yggReseedHostList.size()); + bool isHttps = ind < httpsReseedHostList.size(); + std::string reseedUrl = isHttps ? httpsReseedHostList[ind] : + yggReseedHostList[ind - httpsReseedHostList.size()]; + reseedUrl += "i2pseeds.su3"; + auto num = ReseedFromSU3Url(reseedUrl, isHttps); + if (num > 0) return num; // success + reseedRetries++; + } + LogPrint(eLogWarning, "Reseed: Failed to reseed from servers after 10 attempts"); + return 0; + } - /** - * @brief bootstrap from HTTPS URL with SU3 file - * @param url - * @return number of entries added to netDb - */ - int Reseeder::ReseedFromSU3Url (const std::string& url, bool isHttps) - { - LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url); - std::string su3 = isHttps ? HttpsRequest (url) : YggdrasilRequest (url); - if (su3.length () > 0) - { - std::stringstream s(su3); - return ProcessSU3Stream (s); - } - else - { - LogPrint (eLogWarning, "Reseed: SU3 download failed"); - return 0; - } - } + /** + * @brief bootstrap from HTTPS URL with SU3 file + * @param url + * @return number of entries added to netDb + */ + int Reseeder::ReseedFromSU3Url(const std::string &url, bool isHttps) { + LogPrint(eLogInfo, "Reseed: Downloading SU3 from ", url); + std::string su3 = isHttps ? HttpsRequest(url) : YggdrasilRequest(url); + if (su3.length() > 0) { + std::stringstream s(su3); + return ProcessSU3Stream(s); + } else { + LogPrint(eLogWarning, "Reseed: SU3 download failed"); + return 0; + } + } - int Reseeder::ProcessSU3File (const char * filename) - { - std::ifstream s(filename, std::ifstream::binary); - if (s.is_open ()) - return ProcessSU3Stream (s); - else - { - LogPrint (eLogError, "Reseed: Can't open file ", filename); - return 0; - } - } + int Reseeder::ProcessSU3File(const char *filename) { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open()) + return ProcessSU3Stream(s); + else { + LogPrint(eLogError, "Reseed: Can't open file ", filename); + return 0; + } + } - int Reseeder::ProcessZIPFile (const char * filename) - { - std::ifstream s(filename, std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0, std::ios::end); - auto len = s.tellg (); - s.seekg (0, std::ios::beg); - return ProcessZIPStream (s, len); - } - else - { - LogPrint (eLogError, "Reseed: Can't open file ", filename); - return 0; - } - } + int Reseeder::ProcessZIPFile(const char *filename) { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open()) { + s.seekg(0, std::ios::end); + auto len = s.tellg(); + s.seekg(0, std::ios::beg); + return ProcessZIPStream(s, len); + } else { + LogPrint(eLogError, "Reseed: Can't open file ", filename); + return 0; + } + } - const char SU3_MAGIC_NUMBER[]="I2Psu3"; - int Reseeder::ProcessSU3Stream (std::istream& s) - { - char magicNumber[7]; - s.read (magicNumber, 7); // magic number and zero byte 6 - if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) - { - LogPrint (eLogError, "Reseed: Unexpected SU3 magic number"); - return 0; - } - s.seekg (1, std::ios::cur); // su3 file format version - SigningKeyType signatureType; - s.read ((char *)&signatureType, 2); // signature type - signatureType = be16toh (signatureType); - uint16_t signatureLength; - s.read ((char *)&signatureLength, 2); // signature length - signatureLength = be16toh (signatureLength); - s.seekg (1, std::ios::cur); // unused - uint8_t versionLength; - s.read ((char *)&versionLength, 1); // version length - s.seekg (1, std::ios::cur); // unused - uint8_t signerIDLength; - s.read ((char *)&signerIDLength, 1); // signer ID length - uint64_t contentLength; - s.read ((char *)&contentLength, 8); // content length - contentLength = be64toh (contentLength); - s.seekg (1, std::ios::cur); // unused - uint8_t fileType; - s.read ((char *)&fileType, 1); // file type - if (fileType != 0x00) // zip file - { - LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType); - return 0; - } - s.seekg (1, std::ios::cur); // unused - uint8_t contentType; - s.read ((char *)&contentType, 1); // content type - if (contentType != 0x03) // reseed data - { - LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType); - return 0; - } - s.seekg (12, std::ios::cur); // unused + const char SU3_MAGIC_NUMBER[] = "I2Psu3"; - s.seekg (versionLength, std::ios::cur); // skip version - char signerID[256]; - s.read (signerID, signerIDLength); // signerID - signerID[signerIDLength] = 0; + int Reseeder::ProcessSU3Stream(std::istream &s) { + char magicNumber[7]; + s.read(magicNumber, 7); // magic number and zero byte 6 + if (strcmp(magicNumber, SU3_MAGIC_NUMBER)) { + LogPrint(eLogError, "Reseed: Unexpected SU3 magic number"); + return 0; + } + s.seekg(1, std::ios::cur); // su3 file format version + SigningKeyType signatureType; + s.read((char *) &signatureType, 2); // signature type + signatureType = be16toh(signatureType); + uint16_t signatureLength; + s.read((char *) &signatureLength, 2); // signature length + signatureLength = be16toh(signatureLength); + s.seekg(1, std::ios::cur); // unused + uint8_t versionLength; + s.read((char *) &versionLength, 1); // version length + s.seekg(1, std::ios::cur); // unused + uint8_t signerIDLength; + s.read((char *) &signerIDLength, 1); // signer ID length + uint64_t contentLength; + s.read((char *) &contentLength, 8); // content length + contentLength = be64toh(contentLength); + s.seekg(1, std::ios::cur); // unused + uint8_t fileType; + s.read((char *) &fileType, 1); // file type + if (fileType != 0x00) // zip file + { + LogPrint(eLogError, "Reseed: Can't handle file type ", (int) fileType); + return 0; + } + s.seekg(1, std::ios::cur); // unused + uint8_t contentType; + s.read((char *) &contentType, 1); // content type + if (contentType != 0x03) // reseed data + { + LogPrint(eLogError, "Reseed: Unexpected content type ", (int) contentType); + return 0; + } + s.seekg(12, std::ios::cur); // unused - bool verify; i2p::config::GetOption("reseed.verify", verify); - if (verify) - { - //try to verify signature - auto it = m_SigningKeys.find (signerID); - if (it != m_SigningKeys.end ()) - { - // TODO: implement all signature types - if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) - { - size_t pos = s.tellg (); - size_t tbsLen = pos + contentLength; - uint8_t * tbs = new uint8_t[tbsLen]; - s.seekg (0, std::ios::beg); - s.read ((char *)tbs, tbsLen); - uint8_t * signature = new uint8_t[signatureLength]; - s.read ((char *)signature, signatureLength); - // RSA-raw - { - // calculate digest - uint8_t digest[64]; - SHA512 (tbs, tbsLen, digest); - // encrypt signature - BN_CTX * bnctx = BN_CTX_new (); - BIGNUM * s = BN_new (), * n = BN_new (); - BN_bin2bn (signature, signatureLength, s); - BN_bin2bn (it->second, 512, n); // RSA 4096 assumed - BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n - uint8_t * enSigBuf = new uint8_t[signatureLength]; - i2p::crypto::bn2buf (s, enSigBuf, signatureLength); - // digest is right aligned - // we can't use RSA_verify due wrong padding in SU3 - if (memcmp (enSigBuf + (signatureLength - 64), digest, 64)) - LogPrint (eLogWarning, "Reseed: SU3 signature verification failed"); - else - verify = false; // verified - delete[] enSigBuf; - BN_free (s); BN_free (n); - BN_CTX_free (bnctx); - } + s.seekg(versionLength, std::ios::cur); // skip version + char signerID[256]; + s.read(signerID, signerIDLength); // signerID + signerID[signerIDLength] = 0; - delete[] signature; - delete[] tbs; - s.seekg (pos, std::ios::beg); - } - else - LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported"); - } - else - LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded"); - } + bool verify; + i2p::config::GetOption("reseed.verify", verify); + if (verify) { + //try to verify signature + auto it = m_SigningKeys.find(signerID); + if (it != m_SigningKeys.end()) { + // TODO: implement all signature types + if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) { + size_t pos = s.tellg(); + size_t tbsLen = pos + contentLength; + uint8_t *tbs = new uint8_t[tbsLen]; + s.seekg(0, std::ios::beg); + s.read((char *) tbs, tbsLen); + uint8_t *signature = new uint8_t[signatureLength]; + s.read((char *) signature, signatureLength); + // RSA-raw + { + // calculate digest + uint8_t digest[64]; + SHA512(tbs, tbsLen, digest); + // encrypt signature + BN_CTX *bnctx = BN_CTX_new(); + BIGNUM *s = BN_new(), *n = BN_new(); + BN_bin2bn(signature, signatureLength, s); + BN_bin2bn(it->second, 512, n); // RSA 4096 assumed + BN_mod_exp(s, s, i2p::crypto::GetRSAE(), n, bnctx); // s = s^e mod n + uint8_t *enSigBuf = new uint8_t[signatureLength]; + i2p::crypto::bn2buf(s, enSigBuf, signatureLength); + // digest is right aligned + // we can't use RSA_verify due wrong padding in SU3 + if (memcmp(enSigBuf + (signatureLength - 64), digest, 64)) + LogPrint(eLogWarning, "Reseed: SU3 signature verification failed"); + else + verify = false; // verified + delete[] enSigBuf; + BN_free(s); + BN_free(n); + BN_CTX_free(bnctx); + } - if (verify) // not verified - { - LogPrint (eLogError, "Reseed: SU3 verification failed"); - return 0; - } + delete[] signature; + delete[] tbs; + s.seekg(pos, std::ios::beg); + } else + LogPrint(eLogWarning, "Reseed: Signature type ", signatureType, " is not supported"); + } else + LogPrint(eLogWarning, "Reseed: Certificate for ", signerID, " not loaded"); + } - // handle content - return ProcessZIPStream (s, contentLength); - } + if (verify) // not verified + { + LogPrint(eLogError, "Reseed: SU3 verification failed"); + return 0; + } - const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; - const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; - const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; - int Reseeder::ProcessZIPStream (std::istream& s, uint64_t contentLength) - { - int numFiles = 0; - size_t contentPos = s.tellg (); - while (!s.eof ()) - { - uint32_t signature; - s.read ((char *)&signature, 4); - signature = le32toh (signature); - if (signature == ZIP_HEADER_SIGNATURE) - { - // next local file - s.seekg (2, std::ios::cur); // version - uint16_t bitFlag; - s.read ((char *)&bitFlag, 2); - bitFlag = le16toh (bitFlag); - uint16_t compressionMethod; - s.read ((char *)&compressionMethod, 2); - compressionMethod = le16toh (compressionMethod); - s.seekg (4, std::ios::cur); // skip fields we don't care about - uint32_t compressedSize, uncompressedSize; - uint32_t crc_32; - s.read ((char *)&crc_32, 4); - crc_32 = le32toh (crc_32); - s.read ((char *)&compressedSize, 4); - compressedSize = le32toh (compressedSize); - s.read ((char *)&uncompressedSize, 4); - uncompressedSize = le32toh (uncompressedSize); - uint16_t fileNameLength, extraFieldLength; - s.read ((char *)&fileNameLength, 2); - fileNameLength = le16toh (fileNameLength); - if ( fileNameLength > 255 ) { - // too big - LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength); - return numFiles; - } - s.read ((char *)&extraFieldLength, 2); - extraFieldLength = le16toh (extraFieldLength); - char localFileName[255]; - s.read (localFileName, fileNameLength); - localFileName[fileNameLength] = 0; - s.seekg (extraFieldLength, std::ios::cur); - // take care about data descriptor if presented - if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) - { - size_t pos = s.tellg (); - if (!FindZipDataDescriptor (s)) - { - LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found"); - return numFiles; - } - s.read ((char *)&crc_32, 4); - crc_32 = le32toh (crc_32); - s.read ((char *)&compressedSize, 4); - compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data - s.read ((char *)&uncompressedSize, 4); - uncompressedSize = le32toh (uncompressedSize); + // handle content + return ProcessZIPStream(s, contentLength); + } - // now we know compressed and uncompressed size - s.seekg (pos, std::ios::beg); // back to compressed data - } + const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; + const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; + const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; - LogPrint (eLogDebug, "Reseed: Processing file ", localFileName, " ", compressedSize, " bytes"); - if (!compressedSize) - { - LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped"); - continue; - } + int Reseeder::ProcessZIPStream(std::istream &s, uint64_t contentLength) { + int numFiles = 0; + size_t contentPos = s.tellg(); + while (!s.eof()) { + uint32_t signature; + s.read((char *) &signature, 4); + signature = le32toh (signature); + if (signature == ZIP_HEADER_SIGNATURE) { + // next local file + s.seekg(2, std::ios::cur); // version + uint16_t bitFlag; + s.read((char *) &bitFlag, 2); + bitFlag = le16toh (bitFlag); + uint16_t compressionMethod; + s.read((char *) &compressionMethod, 2); + compressionMethod = le16toh (compressionMethod); + s.seekg(4, std::ios::cur); // skip fields we don't care about + uint32_t compressedSize, uncompressedSize; + uint32_t crc_32; + s.read((char *) &crc_32, 4); + crc_32 = le32toh (crc_32); + s.read((char *) &compressedSize, 4); + compressedSize = le32toh (compressedSize); + s.read((char *) &uncompressedSize, 4); + uncompressedSize = le32toh (uncompressedSize); + uint16_t fileNameLength, extraFieldLength; + s.read((char *) &fileNameLength, 2); + fileNameLength = le16toh (fileNameLength); + if (fileNameLength > 255) { + // too big + LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength); + return numFiles; + } + s.read((char *) &extraFieldLength, 2); + extraFieldLength = le16toh (extraFieldLength); + char localFileName[255]; + s.read(localFileName, fileNameLength); + localFileName[fileNameLength] = 0; + s.seekg(extraFieldLength, std::ios::cur); + // take care about data descriptor if presented + if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) { + size_t pos = s.tellg(); + if (!FindZipDataDescriptor(s)) { + LogPrint(eLogError, "Reseed: SU3 archive data descriptor not found"); + return numFiles; + } + s.read((char *) &crc_32, 4); + crc_32 = le32toh (crc_32); + s.read((char *) &compressedSize, 4); + compressedSize = le32toh (compressedSize) + + 4; // ??? we must consider signature as part of compressed data + s.read((char *) &uncompressedSize, 4); + uncompressedSize = le32toh (uncompressedSize); - uint8_t * compressed = new uint8_t[compressedSize]; - s.read ((char *)compressed, compressedSize); - if (compressionMethod) // we assume Deflate - { - z_stream inflator; - memset (&inflator, 0, sizeof (inflator)); - inflateInit2 (&inflator, -MAX_WBITS); // no zlib header - uint8_t * uncompressed = new uint8_t[uncompressedSize]; - inflator.next_in = compressed; - inflator.avail_in = compressedSize; - inflator.next_out = uncompressed; - inflator.avail_out = uncompressedSize; - int err; - if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0) - { - uncompressedSize -= inflator.avail_out; - if (crc32 (0, uncompressed, uncompressedSize) == crc_32) - { - i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize); - numFiles++; - } - else - LogPrint (eLogError, "Reseed: CRC32 verification failed"); - } - else - LogPrint (eLogError, "Reseed: SU3 decompression error ", err); - delete[] uncompressed; - inflateEnd (&inflator); - } - else // no compression - { - i2p::data::netdb.AddRouterInfo (compressed, compressedSize); - numFiles++; - } - delete[] compressed; - if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) - s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) - } - else - { - if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) - LogPrint (eLogWarning, "Reseed: Missing zip central directory header"); - break; // no more files - } - size_t end = s.tellg (); - if (end - contentPos >= contentLength) - break; // we are beyond contentLength - } - if (numFiles) // check if routers are not outdated - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - int numOutdated = 0; - i2p::data::netdb.VisitRouterInfos ( - [&numOutdated, ts](std::shared_ptr r) - { - if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours - { - LogPrint (eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours"); - numOutdated++; - } - }); - if (numOutdated > numFiles/2) // more than half - { - LogPrint (eLogError, "Reseed: Mammoth's shit\n" - " *_____*\n" - " *_*****_*\n" - " *_(O)_(O)_*\n" - " **____V____**\n" - " **_________**\n" - " **_________**\n" - " *_________*\n" - " ***___***"); - i2p::data::netdb.ClearRouterInfos (); - numFiles = 0; - } - } - return numFiles; - } + // now we know compressed and uncompressed size + s.seekg(pos, std::ios::beg); // back to compressed data + } - const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 }; - bool Reseeder::FindZipDataDescriptor (std::istream& s) - { - size_t nextInd = 0; - while (!s.eof ()) - { - uint8_t nextByte; - s.read ((char *)&nextByte, 1); - if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) - { - nextInd++; - if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE)) - return true; - } - else - nextInd = 0; - } - return false; - } + LogPrint(eLogDebug, "Reseed: Processing file ", localFileName, " ", compressedSize, " bytes"); + if (!compressedSize) { + LogPrint(eLogWarning, "Reseed: Unexpected size 0. Skipped"); + continue; + } - void Reseeder::LoadCertificate (const std::string& filename) - { - SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); - int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); - if (ret) - { - SSL * ssl = SSL_new (ctx); - X509 * cert = SSL_get_certificate (ssl); - // verify - if (cert) - { - // extract issuer name - char name[100]; - X509_NAME_oneline (X509_get_issuer_name(cert), name, 100); - char * cn = strstr (name, "CN="); - if (cn) - { - cn += 3; - char * terminator = strchr (cn, '/'); - if (terminator) terminator[0] = 0; - } - // extract RSA key (we need n only, e = 65537) - const RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert)); - const BIGNUM * n, * e, * d; - RSA_get0_key(key, &n, &e, &d); - PublicKey value; - i2p::crypto::bn2buf (n, value, 512); - if (cn) - m_SigningKeys[cn] = value; - else - LogPrint (eLogError, "Reseed: Can't find CN field in ", filename); - } - SSL_free (ssl); - } - else - LogPrint (eLogError, "Reseed: Can't open certificate file ", filename); - SSL_CTX_free (ctx); - } + uint8_t *compressed = new uint8_t[compressedSize]; + s.read((char *) compressed, compressedSize); + if (compressionMethod) // we assume Deflate + { + z_stream inflator; + memset(&inflator, 0, sizeof(inflator)); + inflateInit2(&inflator, -MAX_WBITS); // no zlib header + uint8_t *uncompressed = new uint8_t[uncompressedSize]; + inflator.next_in = compressed; + inflator.avail_in = compressedSize; + inflator.next_out = uncompressed; + inflator.avail_out = uncompressedSize; + int err; + if ((err = inflate(&inflator, Z_SYNC_FLUSH)) >= 0) { + uncompressedSize -= inflator.avail_out; + if (crc32(0, uncompressed, uncompressedSize) == crc_32) { + i2p::data::netdb.AddRouterInfo(uncompressed, uncompressedSize); + numFiles++; + } else + LogPrint(eLogError, "Reseed: CRC32 verification failed"); + } else + LogPrint(eLogError, "Reseed: SU3 decompression error ", err); + delete[] uncompressed; + inflateEnd(&inflator); + } else // no compression + { + i2p::data::netdb.AddRouterInfo(compressed, compressedSize); + numFiles++; + } + delete[] compressed; + if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) + s.seekg(12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) + } else { + if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) + LogPrint(eLogWarning, "Reseed: Missing zip central directory header"); + break; // no more files + } + size_t end = s.tellg(); + if (end - contentPos >= contentLength) + break; // we are beyond contentLength + } + if (numFiles) // check if routers are not outdated + { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + int numOutdated = 0; + i2p::data::netdb.VisitRouterInfos( + [&numOutdated, ts](std::shared_ptr r) { + if (r && ts > r->GetTimestamp() + + 10 * i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT * 1000LL) // 270 hours + { + LogPrint(eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64(), " is outdated by ", + (ts - r->GetTimestamp()) / 1000LL / 3600LL, " hours"); + numOutdated++; + } + }); + if (numOutdated > numFiles / 2) // more than half + { + LogPrint(eLogError, "Reseed: Mammoth's shit\n" + " *_____*\n" + " *_*****_*\n" + " *_(O)_(O)_*\n" + " **____V____**\n" + " **_________**\n" + " **_________**\n" + " *_________*\n" + " ***___***"); + i2p::data::netdb.ClearRouterInfos(); + numFiles = 0; + } + } + return numFiles; + } - void Reseeder::LoadCertificates () - { - std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed"; + const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = {0x50, 0x4B, 0x07, 0x08}; - std::vector files; - int numCertificates = 0; + bool Reseeder::FindZipDataDescriptor(std::istream &s) { + size_t nextInd = 0; + while (!s.eof()) { + uint8_t nextByte; + s.read((char *) &nextByte, 1); + if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) { + nextInd++; + if (nextInd >= sizeof(ZIP_DATA_DESCRIPTOR_SIGNATURE)) + return true; + } else + nextInd = 0; + } + return false; + } - if (!i2p::fs::ReadDir(certDir, files)) { - LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir); - return; - } + void Reseeder::LoadCertificate(const std::string &filename) { + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + int ret = SSL_CTX_use_certificate_file(ctx, filename.c_str(), SSL_FILETYPE_PEM); + if (ret) { + SSL *ssl = SSL_new(ctx); + X509 *cert = SSL_get_certificate(ssl); + // verify + if (cert) { + // extract issuer name + char name[100]; + X509_NAME_oneline(X509_get_issuer_name(cert), name, 100); + char *cn = strstr(name, "CN="); + if (cn) { + cn += 3; + char *terminator = strchr(cn, '/'); + if (terminator) terminator[0] = 0; + } + // extract RSA key (we need n only, e = 65537) + const RSA *key = EVP_PKEY_get0_RSA(X509_get_pubkey(cert)); + const BIGNUM *n, *e, *d; + RSA_get0_key(key, &n, &e, &d); + PublicKey value; + i2p::crypto::bn2buf(n, value, 512); + if (cn) + m_SigningKeys[cn] = value; + else + LogPrint(eLogError, "Reseed: Can't find CN field in ", filename); + } + SSL_free(ssl); + } else + LogPrint(eLogError, "Reseed: Can't open certificate file ", filename); + SSL_CTX_free(ctx); + } - for (const std::string & file : files) { - if (file.compare(file.size() - 4, 4, ".crt") != 0) { - LogPrint(eLogWarning, "Reseed: Ignoring file ", file); - continue; - } - LoadCertificate (file); - numCertificates++; - } - LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded"); - } + void Reseeder::LoadCertificates() { + std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed"; - std::string Reseeder::HttpsRequest (const std::string& address) - { - i2p::http::URL proxyUrl; - std::string proxy; i2p::config::GetOption("reseed.proxy", proxy); - // check for proxy url - if(proxy.size()) { - // parse - if(proxyUrl.parse(proxy)) { - if (proxyUrl.schema == "http" && !proxyUrl.port) { - proxyUrl.port = 80; - } else if (proxyUrl.schema == "socks" && !proxyUrl.port) { - proxyUrl.port = 1080; - } - // check for valid proxy url schema - if (proxyUrl.schema != "http" && proxyUrl.schema != "socks") { - LogPrint(eLogError, "Reseed: Bad proxy url: ", proxy); - return ""; - } - } else { - LogPrint(eLogError, "Reseed: Bad proxy url: ", proxy); - return ""; - } - } - i2p::http::URL url; - if (!url.parse(address)) { - LogPrint(eLogError, "Reseed: Failed to parse url: ", address); - return ""; - } - url.schema = "https"; - if (!url.port) - url.port = 443; + std::vector files; + int numCertificates = 0; - boost::asio::io_service service; - boost::system::error_code ecode; + if (!i2p::fs::ReadDir(certDir, files)) { + LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir); + return; + } - boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); - ctx.set_verify_mode(boost::asio::ssl::context::verify_none); - boost::asio::ssl::stream s(service, ctx); + for (const std::string &file: files) { + if (file.compare(file.size() - 4, 4, ".crt") != 0) { + LogPrint(eLogWarning, "Reseed: Ignoring file ", file); + continue; + } + LoadCertificate(file); + numCertificates++; + } + LogPrint(eLogInfo, "Reseed: ", numCertificates, " certificates loaded"); + } - if(proxyUrl.schema.size()) - { - // proxy connection - auto it = boost::asio::ip::tcp::resolver(service).resolve ( - boost::asio::ip::tcp::resolver::query (proxyUrl.host, std::to_string(proxyUrl.port)), ecode); - if(!ecode) - { - s.lowest_layer().connect(*it, ecode); - if(!ecode) - { - auto & sock = s.next_layer(); - if(proxyUrl.schema == "http") - { - i2p::http::HTTPReq proxyReq; - i2p::http::HTTPRes proxyRes; - proxyReq.method = "CONNECT"; - proxyReq.version = "HTTP/1.1"; - proxyReq.uri = url.host + ":" + std::to_string(url.port); - auto auth = i2p::http::CreateBasicAuthorizationString (proxyUrl.user, proxyUrl.pass); - if (!auth.empty ()) - proxyReq.AddHeader("Proxy-Authorization", auth); + std::string Reseeder::HttpsRequest(const std::string &address) { + i2p::http::URL proxyUrl; + std::string proxy; + i2p::config::GetOption("reseed.proxy", proxy); + // check for proxy url + if (proxy.size()) { + // parse + if (proxyUrl.parse(proxy)) { + if (proxyUrl.schema == "http" && !proxyUrl.port) { + proxyUrl.port = 80; + } else if (proxyUrl.schema == "socks" && !proxyUrl.port) { + proxyUrl.port = 1080; + } + // check for valid proxy url schema + if (proxyUrl.schema != "http" && proxyUrl.schema != "socks") { + LogPrint(eLogError, "Reseed: Bad proxy url: ", proxy); + return ""; + } + } else { + LogPrint(eLogError, "Reseed: Bad proxy url: ", proxy); + return ""; + } + } + i2p::http::URL url; + if (!url.parse(address)) { + LogPrint(eLogError, "Reseed: Failed to parse url: ", address); + return ""; + } + url.schema = "https"; + if (!url.port) + url.port = 443; - boost::asio::streambuf writebuf, readbuf; - std::ostream out(&writebuf); - out << proxyReq.to_string(); + boost::asio::io_service service; + boost::system::error_code ecode; - boost::asio::write(sock, writebuf.data(), boost::asio::transfer_all(), ecode); - if (ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: HTTP CONNECT write error: ", ecode.message()); - return ""; - } - boost::asio::read_until(sock, readbuf, "\r\n\r\n", ecode); - if (ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message()); - return ""; - } - if(proxyRes.parse(boost::asio::buffer_cast(readbuf.data()), readbuf.size()) <= 0) - { - sock.close(); - LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply"); - return ""; - } - if(proxyRes.code != 200) - { - sock.close(); - LogPrint(eLogError, "Reseed: HTTP CONNECT got bad status: ", proxyRes.code); - return ""; - } - } - else - { - // assume socks if not http, is checked before this for other types - // TODO: support username/password auth etc - uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00}; - uint8_t hs_readbuf[2]; - boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message()); - return ""; - } - boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message()); - return ""; - } - size_t sz = 0; - uint8_t buf[256]; + boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); + ctx.set_verify_mode(boost::asio::ssl::context::verify_none); + boost::asio::ssl::stream s(service, ctx); - buf[0] = 0x05; - buf[1] = 0x01; - buf[2] = 0x00; - buf[3] = 0x03; - sz += 4; - size_t hostsz = url.host.size(); - if(1 + 2 + hostsz + sz > sizeof(buf)) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host); - return ""; - } - buf[4] = (uint8_t) hostsz; - memcpy(buf+5, url.host.c_str(), hostsz); - sz += hostsz + 1; - htobe16buf(buf+sz, url.port); - sz += 2; - boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message()); - return ""; - } - boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message()); - return ""; - } - if(buf[1] != 0x00) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1])); - return ""; - } - } - } - } - } - else - { - // direct connection - auto it = boost::asio::ip::tcp::resolver(service).resolve ( - boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode); - if (!ecode) - { - bool connected = false; - boost::asio::ip::tcp::resolver::iterator end; - while (it != end) - { - boost::asio::ip::tcp::endpoint ep = *it; - if ((ep.address ().is_v4 () && i2p::context.SupportsV4 ()) || - (ep.address ().is_v6 () && i2p::context.SupportsV6 ())) - { - s.lowest_layer().connect (ep, ecode); - if (!ecode) - { - connected = true; - break; - } - } - it++; - } - if (!connected) - { - LogPrint(eLogError, "Reseed: Failed to connect to ", url.host); - return ""; - } - } - } - if (!ecode) - { - SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str ()); - s.handshake (boost::asio::ssl::stream_base::client, ecode); - if (!ecode) - { - LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port); - return ReseedRequest (s, url.to_string()); - } - else - LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ()); - } - else - LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ()); - return ""; - } + if (proxyUrl.schema.size()) { + // proxy connection + auto it = boost::asio::ip::tcp::resolver(service).resolve( + boost::asio::ip::tcp::resolver::query(proxyUrl.host, std::to_string(proxyUrl.port)), ecode); + if (!ecode) { + s.lowest_layer().connect(*it, ecode); + if (!ecode) { + auto &sock = s.next_layer(); + if (proxyUrl.schema == "http") { + i2p::http::HTTPReq proxyReq; + i2p::http::HTTPRes proxyRes; + proxyReq.method = "CONNECT"; + proxyReq.version = "HTTP/1.1"; + proxyReq.uri = url.host + ":" + std::to_string(url.port); + auto auth = i2p::http::CreateBasicAuthorizationString(proxyUrl.user, proxyUrl.pass); + if (!auth.empty()) + proxyReq.AddHeader("Proxy-Authorization", auth); - template - std::string Reseeder::ReseedRequest (Stream& s, const std::string& uri) - { - boost::system::error_code ecode; - i2p::http::HTTPReq req; - req.uri = uri; - req.AddHeader("User-Agent", "Wget/1.11.4"); - req.AddHeader("Connection", "close"); - s.write_some (boost::asio::buffer (req.to_string())); - // read response - std::stringstream rs; - char recv_buf[1024]; size_t l = 0; - do { - l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode); - if (l) rs.write (recv_buf, l); - } while (!ecode && l); - // process response - std::string data = rs.str(); - i2p::http::HTTPRes res; - int len = res.parse(data); - if (len <= 0) { - LogPrint(eLogWarning, "Reseed: Incomplete/broken response from ", uri); - return ""; - } - if (res.code != 200) { - LogPrint(eLogError, "Reseed: Failed to reseed from ", uri, ", http code ", res.code); - return ""; - } - data.erase(0, len); /* drop http headers from response */ - LogPrint(eLogDebug, "Reseed: Got ", data.length(), " bytes of data from ", uri); - if (res.is_chunked()) { - std::stringstream in(data), out; - if (!i2p::http::MergeChunkedResponse(in, out)) { - LogPrint(eLogWarning, "Reseed: Failed to merge chunked response from ", uri); - return ""; - } - LogPrint(eLogDebug, "Reseed: Got ", data.length(), "(", out.tellg(), ") bytes of data from ", uri); - data = out.str(); - } - return data; - } + boost::asio::streambuf writebuf, readbuf; + std::ostream out(&writebuf); + out << proxyReq.to_string(); - std::string Reseeder::YggdrasilRequest (const std::string& address) - { - i2p::http::URL url; - if (!url.parse(address)) - { - LogPrint(eLogError, "Reseed: Failed to parse url: ", address); - return ""; - } - url.schema = "http"; - if (!url.port) url.port = 80; + boost::asio::write(sock, writebuf.data(), boost::asio::transfer_all(), ecode); + if (ecode) { + sock.close(); + LogPrint(eLogError, "Reseed: HTTP CONNECT write error: ", ecode.message()); + return ""; + } + boost::asio::read_until(sock, readbuf, "\r\n\r\n", ecode); + if (ecode) { + sock.close(); + LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message()); + return ""; + } + if (proxyRes.parse(boost::asio::buffer_cast(readbuf.data()), + readbuf.size()) <= 0) { + sock.close(); + LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply"); + return ""; + } + if (proxyRes.code != 200) { + sock.close(); + LogPrint(eLogError, "Reseed: HTTP CONNECT got bad status: ", proxyRes.code); + return ""; + } + } else { + // assume socks if not http, is checked before this for other types + // TODO: support username/password auth etc + uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00}; + uint8_t hs_readbuf[2]; + boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(), + ecode); + if (ecode) { + sock.close(); + LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message()); + return ""; + } + boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode); + if (ecode) { + sock.close(); + LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message()); + return ""; + } + size_t sz = 0; + uint8_t buf[256]; - boost::system::error_code ecode; - boost::asio::io_service service; - boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6()); + buf[0] = 0x05; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = 0x03; + sz += 4; + size_t hostsz = url.host.size(); + if (1 + 2 + hostsz + sz > sizeof(buf)) { + sock.close(); + LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host); + return ""; + } + buf[4] = (uint8_t) hostsz; + memcpy(buf + 5, url.host.c_str(), hostsz); + sz += hostsz + 1; + htobe16buf(buf + sz, url.port); + sz += 2; + boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode); + if (ecode) { + sock.close(); + LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message()); + return ""; + } + boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode); + if (ecode) { + sock.close(); + LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message()); + return ""; + } + if (buf[1] != 0x00) { + sock.close(); + LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1])); + return ""; + } + } + } + } + } else { + // direct connection + auto it = boost::asio::ip::tcp::resolver(service).resolve( + boost::asio::ip::tcp::resolver::query(url.host, std::to_string(url.port)), ecode); + if (!ecode) { + bool connected = false; + boost::asio::ip::tcp::resolver::iterator end; + while (it != end) { + boost::asio::ip::tcp::endpoint ep = *it; + if ((ep.address().is_v4() && i2p::context.SupportsV4()) || + (ep.address().is_v6() && i2p::context.SupportsV6())) { + s.lowest_layer().connect(ep, ecode); + if (!ecode) { + connected = true; + break; + } + } + it++; + } + if (!connected) { + LogPrint(eLogError, "Reseed: Failed to connect to ", url.host); + return ""; + } + } + } + if (!ecode) { + SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str()); + s.handshake(boost::asio::ssl::stream_base::client, ecode); + if (!ecode) { + LogPrint(eLogDebug, "Reseed: Connected to ", url.host, ":", url.port); + return ReseedRequest(s, url.to_string()); + } else + LogPrint(eLogError, "Reseed: SSL handshake failed: ", ecode.message()); + } else + LogPrint(eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message()); + return ""; + } - if (url.host.length () < 2) return ""; // assume [] - auto host = url.host.substr (1, url.host.length () - 2); - LogPrint (eLogDebug, "Reseed: Connecting to Yggdrasil ", url.host, ":", url.port); - s.connect (boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::from_string (host), url.port), ecode); - if (!ecode) - { - LogPrint (eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port); - return ReseedRequest (s, url.to_string()); - } - else - LogPrint (eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message ()); + template + std::string Reseeder::ReseedRequest(Stream &s, const std::string &uri) { + boost::system::error_code ecode; + i2p::http::HTTPReq req; + req.uri = uri; + req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("Connection", "close"); + s.write_some(boost::asio::buffer(req.to_string())); + // read response + std::stringstream rs; + char recv_buf[1024]; + size_t l = 0; + do { + l = s.read_some(boost::asio::buffer(recv_buf, sizeof(recv_buf)), ecode); + if (l) rs.write(recv_buf, l); + } while (!ecode && l); + // process response + std::string data = rs.str(); + i2p::http::HTTPRes res; + int len = res.parse(data); + if (len <= 0) { + LogPrint(eLogWarning, "Reseed: Incomplete/broken response from ", uri); + return ""; + } + if (res.code != 200) { + LogPrint(eLogError, "Reseed: Failed to reseed from ", uri, ", http code ", res.code); + return ""; + } + data.erase(0, len); /* drop http headers from response */ + LogPrint(eLogDebug, "Reseed: Got ", data.length(), " bytes of data from ", uri); + if (res.is_chunked()) { + std::stringstream in(data), out; + if (!i2p::http::MergeChunkedResponse(in, out)) { + LogPrint(eLogWarning, "Reseed: Failed to merge chunked response from ", uri); + return ""; + } + LogPrint(eLogDebug, "Reseed: Got ", data.length(), "(", out.tellg(), ") bytes of data from ", uri); + data = out.str(); + } + return data; + } - return ""; - } -} + std::string Reseeder::YggdrasilRequest(const std::string &address) { + i2p::http::URL url; + if (!url.parse(address)) { + LogPrint(eLogError, "Reseed: Failed to parse url: ", address); + return ""; + } + url.schema = "http"; + if (!url.port) url.port = 80; + + boost::system::error_code ecode; + boost::asio::io_service service; + boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6()); + + if (url.host.length() < 2) return ""; // assume [] + auto host = url.host.substr(1, url.host.length() - 2); + LogPrint(eLogDebug, "Reseed: Connecting to Yggdrasil ", url.host, ":", url.port); + s.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v6::from_string(host), url.port), ecode); + if (!ecode) { + LogPrint(eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port); + return ReseedRequest(s, url.to_string()); + } else + LogPrint(eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message()); + + return ""; + } + } } diff --git a/libi2pd/Reseed.h b/libi2pd/Reseed.h index a6de6fa4..f600bf62 100644 --- a/libi2pd/Reseed.h +++ b/libi2pd/Reseed.h @@ -16,46 +16,52 @@ #include "Identity.h" #include "Crypto.h" -namespace i2p -{ -namespace data -{ +namespace i2p { + namespace data { - class Reseeder - { - typedef Tag<512> PublicKey; + class Reseeder { + typedef Tag<512> PublicKey; - public: + public: - Reseeder(); - ~Reseeder(); - void Bootstrap (); - int ReseedFromServers (); - int ProcessSU3File (const char * filename); - int ProcessZIPFile (const char * filename); + Reseeder(); - void LoadCertificates (); + ~Reseeder(); - private: + void Bootstrap(); - int ReseedFromSU3Url (const std::string& url, bool isHttps = true); - void LoadCertificate (const std::string& filename); + int ReseedFromServers(); - int ProcessSU3Stream (std::istream& s); - int ProcessZIPStream (std::istream& s, uint64_t contentLength); + int ProcessSU3File(const char *filename); - bool FindZipDataDescriptor (std::istream& s); + int ProcessZIPFile(const char *filename); - std::string HttpsRequest (const std::string& address); - std::string YggdrasilRequest (const std::string& address); - template - std::string ReseedRequest (Stream& s, const std::string& uri); + void LoadCertificates(); - private: + private: - std::map 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 + std::string ReseedRequest(Stream &s, const std::string &uri); + + private: + + std::map m_SigningKeys; + }; + } } #endif diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 77d3763d..cce1aec6 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -22,1257 +22,1151 @@ #include "ECIESX25519AEADRatchetSession.h" #include "RouterContext.h" -namespace i2p -{ - RouterContext context; +namespace i2p { + RouterContext context; - RouterContext::RouterContext (): - m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), - m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), - m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID) - { - } + RouterContext::RouterContext() : + m_LastUpdateTime(0), m_AcceptsTunnels(true), m_IsFloodfill(false), + m_ShareRatio(100), m_Status(eRouterStatusUnknown), m_StatusV6(eRouterStatusUnknown), + m_Error(eRouterErrorNone), m_ErrorV6(eRouterErrorNone), m_NetID(I2PD_NET_ID) { + } - void RouterContext::Init () - { - srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); - m_StartupTime = std::chrono::steady_clock::now(); + void RouterContext::Init() { + srand(i2p::util::GetMillisecondsSinceEpoch() % 1000); + m_StartupTime = std::chrono::steady_clock::now(); - if (!Load ()) - CreateNewRouter (); - m_Decryptor = m_Keys.CreateDecryptor (nullptr); - m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr); - UpdateRouterInfo (); - i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ()); - m_ECIESSession = std::make_shared(m_InitialNoiseState); - } + if (!Load()) + CreateNewRouter(); + m_Decryptor = m_Keys.CreateDecryptor(nullptr); + m_TunnelDecryptor = m_Keys.CreateDecryptor(nullptr); + UpdateRouterInfo(); + i2p::crypto::InitNoiseNState(m_InitialNoiseState, GetIdentity()->GetEncryptionPublicKey()); + m_ECIESSession = std::make_shared(m_InitialNoiseState); + } - void RouterContext::CreateNewRouter () - { - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); - SaveKeys (); - NewRouterInfo (); - } + void RouterContext::CreateNewRouter() { + m_Keys = i2p::data::PrivateKeys::CreateRandomKeys(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); + SaveKeys(); + NewRouterInfo(); + } - void RouterContext::NewRouterInfo () - { - i2p::data::LocalRouterInfo routerInfo; - routerInfo.SetRouterIdentity (GetIdentity ()); - uint16_t port; i2p::config::GetOption("port", port); - if (!port) port = SelectRandomPort (); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ssu; i2p::config::GetOption("ssu", ssu); - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); - bool nat; i2p::config::GetOption("nat", nat); + void RouterContext::NewRouterInfo() { + i2p::data::LocalRouterInfo routerInfo; + routerInfo.SetRouterIdentity(GetIdentity()); + uint16_t port; + i2p::config::GetOption("port", port); + if (!port) port = SelectRandomPort(); + bool ipv4; + i2p::config::GetOption("ipv4", ipv4); + bool ipv6; + i2p::config::GetOption("ipv6", ipv6); + bool ssu; + i2p::config::GetOption("ssu", ssu); + bool ntcp2; + i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ssu2; + i2p::config::GetOption("ssu2.enabled", ssu2); + bool ygg; + i2p::config::GetOption("meshnets.yggdrasil", ygg); + bool nat; + i2p::config::GetOption("nat", nat); - if ((ntcp2 || ygg) && !m_NTCP2Keys) - NewNTCP2Keys (); - if (ssu2 && !m_SSU2Keys) - NewSSU2Keys (); - bool ntcp2Published = false; - if (ntcp2) - { - i2p::config::GetOption("ntcp2.published", ntcp2Published); - if (ntcp2Published) - { - std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); - if (!ntcp2proxy.empty ()) ntcp2Published = false; - } - } - bool ssu2Published = false; - if (ssu2) - i2p::config::GetOption("ssu2.published", ssu2Published); - uint8_t caps = 0, addressCaps = 0; - if (ipv4) - { - std::string host = "127.0.0.1"; - if (!i2p::config::IsDefault("host")) - i2p::config::GetOption("host", host); - else if (!nat) - { - // we have no NAT so set external address from local address - std::string address4; i2p::config::GetOption("address4", address4); - if (!address4.empty ()) host = address4; - } + if ((ntcp2 || ygg) && !m_NTCP2Keys) + NewNTCP2Keys(); + if (ssu2 && !m_SSU2Keys) + NewSSU2Keys(); + bool ntcp2Published = false; + if (ntcp2) { + i2p::config::GetOption("ntcp2.published", ntcp2Published); + if (ntcp2Published) { + std::string ntcp2proxy; + i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); + if (!ntcp2proxy.empty()) ntcp2Published = false; + } + } + bool ssu2Published = false; + if (ssu2) + i2p::config::GetOption("ssu2.published", ssu2Published); + uint8_t caps = 0, addressCaps = 0; + if (ipv4) { + std::string host = "127.0.0.1"; + if (!i2p::config::IsDefault("host")) + i2p::config::GetOption("host", host); + else if (!nat) { + // we have no NAT so set external address from local address + std::string address4; + i2p::config::GetOption("address4", address4); + if (!address4.empty()) host = address4; + } - if (ntcp2) - { - if (ntcp2Published) - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port); - else // add non-published NTCP2 address - { - addressCaps = i2p::data::RouterInfo::AddressCaps::eV4; - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); - } - } - if (ssu) - { - routerInfo.AddSSUAddress (host.c_str(), port, nullptr); - caps |= i2p::data::RouterInfo::eReachable; // R - } - if (ssu2) - { - if (ssu2Published) - { - uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; - routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port); - } - else - { - addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; - routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); - } - } - } - if (ipv6) - { - std::string host = "::1"; - if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only - i2p::config::GetOption("host", host); - else - { - std::string address6; i2p::config::GetOption("address6", address6); - if (!address6.empty ()) host = address6; - } + if (ntcp2) { + if (ntcp2Published) + routerInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, + boost::asio::ip::address_v4::from_string(host), port); + else // add non-published NTCP2 address + { + addressCaps = i2p::data::RouterInfo::AddressCaps::eV4; + routerInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + } + } + if (ssu) { + routerInfo.AddSSUAddress(host.c_str(), port, nullptr); + caps |= i2p::data::RouterInfo::eReachable; // R + } + if (ssu2) { + if (ssu2Published) { + uint16_t ssu2Port; + i2p::config::GetOption("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + routerInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + boost::asio::ip::address_v4::from_string(host), ssu2Port); + } else { + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; + routerInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); + } + } + } + if (ipv6) { + std::string host = "::1"; + if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only + i2p::config::GetOption("host", host); + else { + std::string address6; + i2p::config::GetOption("address6", address6); + if (!address6.empty()) host = address6; + } - if (ntcp2) - { - if (ntcp2Published) - { - std::string ntcp2Host; - if (!i2p::config::IsDefault ("ntcp2.addressv6")) - i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); - else - ntcp2Host = host; - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port); - } - else - { - if (!ipv4) // no other ntcp2 addresses yet - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); - addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; - } - } - if (ssu) - { - routerInfo.AddSSUAddress (host.c_str(), port, nullptr); - caps |= i2p::data::RouterInfo::eReachable; // R - } - if (ssu2) - { - if (ssu2Published) - { - uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; - routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port); - } - else - { - if (!ipv4) // no other ssu2 addresses yet - routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); - addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; - } - } - } - if (ygg) - { - auto yggaddr = i2p::util::net::GetYggdrasilAddress (); - if (!yggaddr.is_unspecified ()) - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port); - } + if (ntcp2) { + if (ntcp2Published) { + std::string ntcp2Host; + if (!i2p::config::IsDefault("ntcp2.addressv6")) + i2p::config::GetOption("ntcp2.addressv6", ntcp2Host); + else + ntcp2Host = host; + routerInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, + boost::asio::ip::address_v6::from_string(ntcp2Host), port); + } else { + if (!ipv4) // no other ntcp2 addresses yet + routerInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + } + } + if (ssu) { + routerInfo.AddSSUAddress(host.c_str(), port, nullptr); + caps |= i2p::data::RouterInfo::eReachable; // R + } + if (ssu2) { + if (ssu2Published) { + uint16_t ssu2Port; + i2p::config::GetOption("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + routerInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + boost::asio::ip::address_v6::from_string(host), ssu2Port); + } else { + if (!ipv4) // no other ssu2 addresses yet + routerInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + } + } + } + if (ygg) { + auto yggaddr = i2p::util::net::GetYggdrasilAddress(); + if (!yggaddr.is_unspecified()) + routerInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port); + } - if (addressCaps) - routerInfo.SetUnreachableAddressesTransportCaps (addressCaps); - routerInfo.UpdateCaps (caps); // caps + L - routerInfo.SetProperty ("netId", std::to_string (m_NetID)); - routerInfo.SetProperty ("router.version", I2P_VERSION); - routerInfo.CreateBuffer (m_Keys); - m_RouterInfo.SetRouterIdentity (GetIdentity ()); - m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); - } + if (addressCaps) + routerInfo.SetUnreachableAddressesTransportCaps(addressCaps); + routerInfo.UpdateCaps(caps); // caps + L + routerInfo.SetProperty("netId", std::to_string(m_NetID)); + routerInfo.SetProperty("router.version", I2P_VERSION); + routerInfo.CreateBuffer(m_Keys); + m_RouterInfo.SetRouterIdentity(GetIdentity()); + m_RouterInfo.Update(routerInfo.GetBuffer(), routerInfo.GetBufferLen()); + } - uint16_t RouterContext::SelectRandomPort () const - { - uint16_t port = rand () % (30777 - 9111) + 9111; // I2P network ports range - if (port == 9150) port = 9151; // Tor browser - return port; - } - - void RouterContext::UpdateRouterInfo () - { - m_RouterInfo.CreateBuffer (m_Keys); - m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO)); - m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); - } + uint16_t RouterContext::SelectRandomPort() const { + uint16_t port = rand() % (30777 - 9111) + 9111; // I2P network ports range + if (port == 9150) port = 9151; // Tor browser + return port; + } - void RouterContext::NewNTCP2Keys () - { - m_NTCP2StaticKeys.reset (new i2p::crypto::X25519Keys ()); - m_NTCP2StaticKeys->GenerateKeys (); - m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); - m_NTCP2StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey); - memcpy (m_NTCP2Keys->staticPublicKey, m_NTCP2StaticKeys->GetPublicKey (), 32); - RAND_bytes (m_NTCP2Keys->iv, 16); - // save - std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out); - fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); - } + void RouterContext::UpdateRouterInfo() { + m_RouterInfo.CreateBuffer(m_Keys); + m_RouterInfo.SaveToFile(i2p::fs::DataDirPath(ROUTER_INFO)); + m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch(); + } - void RouterContext::NewSSU2Keys () - { - m_SSU2StaticKeys.reset (new i2p::crypto::X25519Keys ()); - m_SSU2StaticKeys->GenerateKeys (); - m_SSU2Keys.reset (new SSU2PrivateKeys ()); - m_SSU2StaticKeys->GetPrivateKey (m_SSU2Keys->staticPrivateKey); - memcpy (m_SSU2Keys->staticPublicKey, m_SSU2StaticKeys->GetPublicKey (), 32); - RAND_bytes (m_SSU2Keys->intro, 32); - // save - std::ofstream fk (i2p::fs::DataDirPath (SSU2_KEYS), std::ofstream::binary | std::ofstream::out); - fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); - } + void RouterContext::NewNTCP2Keys() { + m_NTCP2StaticKeys.reset(new i2p::crypto::X25519Keys()); + m_NTCP2StaticKeys->GenerateKeys(); + m_NTCP2Keys.reset(new NTCP2PrivateKeys()); + m_NTCP2StaticKeys->GetPrivateKey(m_NTCP2Keys->staticPrivateKey); + memcpy(m_NTCP2Keys->staticPublicKey, m_NTCP2StaticKeys->GetPublicKey(), 32); + RAND_bytes(m_NTCP2Keys->iv, 16); + // save + std::ofstream fk(i2p::fs::DataDirPath(NTCP2_KEYS), std::ofstream::binary | std::ofstream::out); + fk.write((char *) m_NTCP2Keys.get(), sizeof(NTCP2PrivateKeys)); + } - bool RouterContext::IsSSU2Only () const - { - auto transports = m_RouterInfo.GetCompatibleTransports (false); - return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) && - !(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6)); - } + void RouterContext::NewSSU2Keys() { + m_SSU2StaticKeys.reset(new i2p::crypto::X25519Keys()); + m_SSU2StaticKeys->GenerateKeys(); + m_SSU2Keys.reset(new SSU2PrivateKeys()); + m_SSU2StaticKeys->GetPrivateKey(m_SSU2Keys->staticPrivateKey); + memcpy(m_SSU2Keys->staticPublicKey, m_SSU2StaticKeys->GetPublicKey(), 32); + RAND_bytes(m_SSU2Keys->intro, 32); + // save + std::ofstream fk(i2p::fs::DataDirPath(SSU2_KEYS), std::ofstream::binary | std::ofstream::out); + fk.write((char *) m_SSU2Keys.get(), sizeof(SSU2PrivateKeys)); + } - void RouterContext::SetStatus (RouterStatus status) - { - if (status != m_Status) - { - m_Status = status; - m_Error = eRouterErrorNone; - switch (m_Status) - { - case eRouterStatusOK: - SetReachable (true, false); // ipv4 - break; - case eRouterStatusFirewalled: - SetUnreachable (true, false); // ipv4 - break; - default: - ; - } - } - } + bool RouterContext::IsSSU2Only() const { + auto transports = m_RouterInfo.GetCompatibleTransports(false); + return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) && + !(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6)); + } - void RouterContext::SetStatusSSU2 (RouterStatus status) - { - if (IsSSU2Only ()) - SetStatus (status); - } + void RouterContext::SetStatus(RouterStatus status) { + if (status != m_Status) { + m_Status = status; + m_Error = eRouterErrorNone; + switch (m_Status) { + case eRouterStatusOK: + SetReachable(true, false); // ipv4 + break; + case eRouterStatusFirewalled: + SetUnreachable(true, false); // ipv4 + break; + default:; + } + } + } - void RouterContext::SetStatusV6 (RouterStatus status) - { - if (status != m_StatusV6) - { - m_StatusV6 = status; - m_ErrorV6 = eRouterErrorNone; - switch (m_StatusV6) - { - case eRouterStatusOK: - SetReachable (false, true); // ipv6 - break; - case eRouterStatusFirewalled: - SetUnreachable (false, true); // ipv6 - break; - default: - ; - } - } - } + void RouterContext::SetStatusSSU2(RouterStatus status) { + if (IsSSU2Only()) + SetStatus(status); + } - void RouterContext::SetStatusV6SSU2 (RouterStatus status) - { - if (IsSSU2Only ()) - SetStatusV6 (status); - } + void RouterContext::SetStatusV6(RouterStatus status) { + if (status != m_StatusV6) { + m_StatusV6 = status; + m_ErrorV6 = eRouterErrorNone; + switch (m_StatusV6) { + case eRouterStatusOK: + SetReachable(false, true); // ipv6 + break; + case eRouterStatusFirewalled: + SetUnreachable(false, true); // ipv6 + break; + default:; + } + } + } - void RouterContext::UpdatePort (int port) - { - bool updated = false; - for (auto& address : m_RouterInfo.GetAddresses ()) - { - if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ())) - { - address->port = port; - updated = true; - } - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::SetStatusV6SSU2(RouterStatus status) { + if (IsSSU2Only()) + SetStatusV6(status); + } - void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg) - { - if (!m_NTCP2Keys) return; - bool updated = false; - for (auto& address : m_RouterInfo.GetAddresses ()) - { - if (address->IsNTCP2 () && (address->port != port || address->published != publish)) - { - bool isAddr = v4 && address->IsV4 (); - if (!isAddr && (v6 || ygg)) - { - if (i2p::util::net::IsYggdrasilAddress (address->host)) - isAddr = ygg; - else - isAddr = v6 && address->IsV6 (); - } - if (isAddr) - { - if (!port && !address->port) port = SelectRandomPort (); - if (port) address->port = port; - address->published = publish; - memcpy (address->i, m_NTCP2Keys->iv, 16); - updated = true; - } - } - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::UpdatePort(int port) { + bool updated = false; + for (auto &address: m_RouterInfo.GetAddresses()) { + if (address->port != port && + (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only())) { + address->port = port; + updated = true; + } + } + if (updated) + UpdateRouterInfo(); + } - void RouterContext::UpdateNTCP2Address (bool enable) - { - auto& addresses = m_RouterInfo.GetAddresses (); - bool found = false, updated = false; - for (auto it = addresses.begin (); it != addresses.end ();) - { - if ((*it)->IsNTCP2 ()) - { - found = true; - if (enable) - { - (*it)->s = m_NTCP2Keys->staticPublicKey; - memcpy ((*it)->i, m_NTCP2Keys->iv, 16); - it++; - } - else - it = addresses.erase (it); - updated = true; - } - else - it++; - } - if (enable && !found) - { - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); - updated = true; - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::PublishNTCP2Address(int port, bool publish, bool v4, bool v6, bool ygg) { + if (!m_NTCP2Keys) return; + bool updated = false; + for (auto &address: m_RouterInfo.GetAddresses()) { + if (address->IsNTCP2() && (address->port != port || address->published != publish)) { + bool isAddr = v4 && address->IsV4(); + if (!isAddr && (v6 || ygg)) { + if (i2p::util::net::IsYggdrasilAddress(address->host)) + isAddr = ygg; + else + isAddr = v6 && address->IsV6(); + } + if (isAddr) { + if (!port && !address->port) port = SelectRandomPort(); + if (port) address->port = port; + address->published = publish; + memcpy(address->i, m_NTCP2Keys->iv, 16); + updated = true; + } + } + } + if (updated) + UpdateRouterInfo(); + } - void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6) - { - if (!m_SSU2Keys) return; - int newPort = 0; - if (!port) - { - for (const auto& address : m_RouterInfo.GetAddresses ()) - if (address->port) - { - newPort = address->port; - break; - } - if (!newPort) newPort = SelectRandomPort (); - } - bool updated = false; - for (auto& address : m_RouterInfo.GetAddresses ()) - { - if (address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) && - ((v4 && address->IsV4 ()) || (v6 && address->IsV6 ()))) - { - if (port) address->port = port; - else if (!address->port) address->port = newPort; - address->published = publish; - if (publish) - address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); - else - address->caps &= ~(i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); - updated = true; - } - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::UpdateNTCP2Address(bool enable) { + auto &addresses = m_RouterInfo.GetAddresses(); + bool found = false, updated = false; + for (auto it = addresses.begin(); it != addresses.end();) { + if ((*it)->IsNTCP2()) { + found = true; + if (enable) { + (*it)->s = m_NTCP2Keys->staticPublicKey; + memcpy((*it)->i, m_NTCP2Keys->iv, 16); + it++; + } else + it = addresses.erase(it); + updated = true; + } else + it++; + } + if (enable && !found) { + m_RouterInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + updated = true; + } + if (updated) + UpdateRouterInfo(); + } - void RouterContext::UpdateSSU2Address (bool enable) - { - auto& addresses = m_RouterInfo.GetAddresses (); - bool found = false, updated = false; - for (auto it = addresses.begin (); it != addresses.end ();) - { - if ((*it)->IsSSU2 ()) - { - found = true; - if (enable) - { - (*it)->s = m_SSU2Keys->staticPublicKey; - (*it)->i = m_SSU2Keys->intro; - it++; - } - else - it = addresses.erase (it); - updated = true; - } - else - it++; - } - if (enable && !found) - { - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool published; i2p::config::GetOption("ntcp2.published", published); - if (published) - { - if (ipv4) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV4); - if (ipv6) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV6); - } - else - { - uint8_t addressCaps = 0; - if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; - if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; - m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps); - } - updated = true; - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::PublishSSU2Address(int port, bool publish, bool v4, bool v6) { + if (!m_SSU2Keys) return; + int newPort = 0; + if (!port) { + for (const auto &address: m_RouterInfo.GetAddresses()) + if (address->port) { + newPort = address->port; + break; + } + if (!newPort) newPort = SelectRandomPort(); + } + bool updated = false; + for (auto &address: m_RouterInfo.GetAddresses()) { + if (address->IsSSU2() && (!address->port || address->port != port || address->published != publish) && + ((v4 && address->IsV4()) || (v6 && address->IsV6()))) { + if (port) address->port = port; + else if (!address->port) address->port = newPort; + address->published = publish; + if (publish) + address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); + else + address->caps &= ~(i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); + updated = true; + } + } + if (updated) + UpdateRouterInfo(); + } - void RouterContext::UpdateAddress (const boost::asio::ip::address& host) - { - bool updated = false; - for (auto& address : m_RouterInfo.GetAddresses ()) - { - if (address->host != host && address->IsCompatible (host) && - !i2p::util::net::IsYggdrasilAddress (address->host)) - { - // update host - address->host = host; - updated = true; - } - if (host.is_v6 () && address->IsV6 () && address->ssu && - (!address->ssu->mtu || updated)) - { - // update MTU - auto mtu = i2p::util::net::GetMTU (host); - if (mtu) - { - LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu); - int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ()); - if (mtu > maxMTU) - { - mtu = maxMTU; - LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes"); - } - if (mtu && !address->IsSSU2 ()) // SSU1 - mtu = (mtu >> 4) << 4; // round to multiple of 16 - address->ssu->mtu = mtu; - updated = true; - } - } - } - auto ts = i2p::util::GetSecondsSinceEpoch (); - if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) - UpdateRouterInfo (); - } + void RouterContext::UpdateSSU2Address(bool enable) { + auto &addresses = m_RouterInfo.GetAddresses(); + bool found = false, updated = false; + for (auto it = addresses.begin(); it != addresses.end();) { + if ((*it)->IsSSU2()) { + found = true; + if (enable) { + (*it)->s = m_SSU2Keys->staticPublicKey; + (*it)->i = m_SSU2Keys->intro; + it++; + } else + it = addresses.erase(it); + updated = true; + } else + it++; + } + if (enable && !found) { + bool ipv4; + i2p::config::GetOption("ipv4", ipv4); + bool ipv6; + i2p::config::GetOption("ipv6", ipv6); + bool published; + i2p::config::GetOption("ntcp2.published", published); + if (published) { + if (ipv4) + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + i2p::data::RouterInfo::AddressCaps::eV4); + if (ipv6) + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + i2p::data::RouterInfo::AddressCaps::eV6); + } else { + uint8_t addressCaps = 0; + if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; + if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps); + } + updated = true; + } + if (updated) + UpdateRouterInfo(); + } - bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer) - { - bool ret = m_RouterInfo.AddIntroducer (introducer); - if (ret) - UpdateRouterInfo (); - return ret; - } + void RouterContext::UpdateAddress(const boost::asio::ip::address &host) { + bool updated = false; + for (auto &address: m_RouterInfo.GetAddresses()) { + if (address->host != host && address->IsCompatible(host) && + !i2p::util::net::IsYggdrasilAddress(address->host)) { + // update host + address->host = host; + updated = true; + } + if (host.is_v6() && address->IsV6() && address->ssu && + (!address->ssu->mtu || updated)) { + // update MTU + auto mtu = i2p::util::net::GetMTU(host); + if (mtu) { + LogPrint(eLogDebug, "Router: Our v6 MTU=", mtu); + int maxMTU = i2p::util::net::GetMaxMTU(host.to_v6()); + if (mtu > maxMTU) { + mtu = maxMTU; + LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes"); + } + if (mtu && !address->IsSSU2()) // SSU1 + mtu = (mtu >> 4) << 4; // round to multiple of 16 + address->ssu->mtu = mtu; + updated = true; + } + } + } + auto ts = i2p::util::GetSecondsSinceEpoch(); + if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) + UpdateRouterInfo(); + } - void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) - { - if (m_RouterInfo.RemoveIntroducer (e)) - UpdateRouterInfo (); - } + bool RouterContext::AddIntroducer(const i2p::data::RouterInfo::Introducer &introducer) { + bool ret = m_RouterInfo.AddIntroducer(introducer); + if (ret) + UpdateRouterInfo(); + return ret; + } - bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4) - { - if (!IsSSU2Only ()) return false; - bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4); - if (ret) - UpdateRouterInfo (); - return ret; - } + void RouterContext::RemoveIntroducer(const boost::asio::ip::udp::endpoint &e) { + if (m_RouterInfo.RemoveIntroducer(e)) + UpdateRouterInfo(); + } - void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4) - { - if (!IsSSU2Only ()) return; - if (m_RouterInfo.RemoveSSU2Introducer (h, v4)) - UpdateRouterInfo (); - } + bool RouterContext::AddSSU2Introducer(const i2p::data::RouterInfo::Introducer &introducer, bool v4) { + if (!IsSSU2Only()) return false; + bool ret = m_RouterInfo.AddSSU2Introducer(introducer, v4); + if (ret) + UpdateRouterInfo(); + return ret; + } - void RouterContext::ClearSSU2Introducers (bool v4) - { - bool updated = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr : addresses) - if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) && - addr->ssu && !addr->ssu->introducers.empty ()) - { - addr->ssu->introducers.clear (); - updated = true; - } - if (updated) - UpdateRouterInfo (); - } - - void RouterContext::SetFloodfill (bool floodfill) - { - m_IsFloodfill = floodfill; - if (floodfill) - m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill); - else - { - m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill); - // we don't publish number of routers and leaseset for non-floodfill - m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS); - m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS); - } - UpdateRouterInfo (); - } + void RouterContext::RemoveSSU2Introducer(const i2p::data::IdentHash &h, bool v4) { + if (!IsSSU2Only()) return; + if (m_RouterInfo.RemoveSSU2Introducer(h, v4)) + UpdateRouterInfo(); + } - std::string RouterContext::GetFamily () const - { - return m_RouterInfo.GetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY); - } + void RouterContext::ClearSSU2Introducers(bool v4) { + bool updated = false; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) + if (addr->IsSSU2() && ((v4 && addr->IsV4()) || (!v4 && addr->IsV6())) && + addr->ssu && !addr->ssu->introducers.empty()) { + addr->ssu->introducers.clear(); + updated = true; + } + if (updated) + UpdateRouterInfo(); + } - void RouterContext::SetFamily (const std::string& family) - { - std::string signature; - if (family.length () > 0) - signature = i2p::data::CreateFamilySignature (family, GetIdentHash ()); - if (signature.length () > 0) - { - m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY, family); - m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG, signature); - } - else - { - m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY); - m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG); - } - } + void RouterContext::SetFloodfill(bool floodfill) { + m_IsFloodfill = floodfill; + if (floodfill) + m_RouterInfo.UpdateCaps(m_RouterInfo.GetCaps() | i2p::data::RouterInfo::eFloodfill); + else { + m_RouterInfo.UpdateCaps(m_RouterInfo.GetCaps() & ~i2p::data::RouterInfo::eFloodfill); + // we don't publish number of routers and leaseset for non-floodfill + m_RouterInfo.DeleteProperty(i2p::data::ROUTER_INFO_PROPERTY_LEASESETS); + m_RouterInfo.DeleteProperty(i2p::data::ROUTER_INFO_PROPERTY_ROUTERS); + } + UpdateRouterInfo(); + } - void RouterContext::SetBandwidth (char L) - { - uint32_t limit = 0; - enum { low, high, extra, unlim } type = high; - /* detect parameters */ - switch (L) - { - case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break; - case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = 48; type = low; break; - case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 64; type = high; break; - case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break; - case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break; - case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break; - case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s - default: - limit = 48; type = low; - } - /* update caps & flags in RI */ - auto caps = m_RouterInfo.GetCaps (); - caps &= ~i2p::data::RouterInfo::eHighBandwidth; - caps &= ~i2p::data::RouterInfo::eExtraBandwidth; - switch (type) - { - case low : /* not set */; break; - case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' - case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; + std::string RouterContext::GetFamily() const { + return m_RouterInfo.GetProperty(i2p::data::ROUTER_INFO_PROPERTY_FAMILY); + } + + void RouterContext::SetFamily(const std::string &family) { + std::string signature; + if (family.length() > 0) + signature = i2p::data::CreateFamilySignature(family, GetIdentHash()); + if (signature.length() > 0) { + m_RouterInfo.SetProperty(i2p::data::ROUTER_INFO_PROPERTY_FAMILY, family); + m_RouterInfo.SetProperty(i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG, signature); + } else { + m_RouterInfo.DeleteProperty(i2p::data::ROUTER_INFO_PROPERTY_FAMILY); + m_RouterInfo.DeleteProperty(i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG); + } + } + + void RouterContext::SetBandwidth(char L) { + uint32_t limit = 0; + enum { + low, high, extra, unlim + } type = high; + /* detect parameters */ + switch (L) { + case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : + limit = 12; + type = low; + break; + case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : + limit = 48; + type = low; + break; + case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : + limit = 64; + type = high; + break; + case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : + limit = 128; + type = high; + break; + case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : + limit = 256; + type = high; + break; + case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : + limit = 2048; + type = extra; + break; + case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : + limit = 1000000; + type = unlim; + break; // 1Gbyte/s + default: + limit = 48; + type = low; + } + /* update caps & flags in RI */ + auto caps = m_RouterInfo.GetCaps(); + caps &= ~i2p::data::RouterInfo::eHighBandwidth; + caps &= ~i2p::data::RouterInfo::eExtraBandwidth; + switch (type) { + case low : /* not set */; + break; + case extra : + caps |= i2p::data::RouterInfo::eExtraBandwidth; + break; // 'P' + case unlim : + caps |= i2p::data::RouterInfo::eExtraBandwidth; #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; + [[fallthrough]]; #endif - // no break here, extra + high means 'X' - case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; - } - m_RouterInfo.UpdateCaps (caps); - UpdateRouterInfo (); - m_BandwidthLimit = limit; - } + // no break here, extra + high means 'X' + case high : + caps |= i2p::data::RouterInfo::eHighBandwidth; + break; + } + m_RouterInfo.UpdateCaps(caps); + UpdateRouterInfo(); + m_BandwidthLimit = limit; + } - void RouterContext::SetBandwidth (int limit) - { - if (limit > 2000) { SetBandwidth('X'); } - else if (limit > 256) { SetBandwidth('P'); } - else if (limit > 128) { SetBandwidth('O'); } - else if (limit > 64) { SetBandwidth('N'); } - else if (limit > 48) { SetBandwidth('M'); } - else if (limit > 12) { SetBandwidth('L'); } - else { SetBandwidth('K'); } - m_BandwidthLimit = limit; // set precise limit - } + void RouterContext::SetBandwidth(int limit) { + if (limit > 2000) { SetBandwidth('X'); } + else if (limit > 256) { SetBandwidth('P'); } + else if (limit > 128) { SetBandwidth('O'); } + else if (limit > 64) { SetBandwidth('N'); } + else if (limit > 48) { SetBandwidth('M'); } + else if (limit > 12) { SetBandwidth('L'); } + else { SetBandwidth('K'); } + m_BandwidthLimit = limit; // set precise limit + } - void RouterContext::SetShareRatio (int percents) - { - if (percents < 0) percents = 0; - if (percents > 100) percents = 100; - m_ShareRatio = percents; - } + void RouterContext::SetShareRatio(int percents) { + if (percents < 0) percents = 0; + if (percents > 100) percents = 100; + m_ShareRatio = percents; + } - bool RouterContext::IsUnreachable () const - { - return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable; - } + bool RouterContext::IsUnreachable() const { + return m_RouterInfo.GetCaps() & i2p::data::RouterInfo::eUnreachable; + } - void RouterContext::RemoveNTCPAddress (bool v4only) - { - bool updated = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto it = addresses.begin (); it != addresses.end ();) - { - if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () && - (!v4only || (*it)->host.is_v4 ())) - { - it = addresses.erase (it); - updated = true; - if (v4only) break; // otherwise might be more than one address - } - else - ++it; - } - if (updated) - m_RouterInfo.UpdateSupportedTransports (); - } + void RouterContext::RemoveNTCPAddress(bool v4only) { + bool updated = false; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto it = addresses.begin(); it != addresses.end();) { + if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2() && + (!v4only || (*it)->host.is_v4())) { + it = addresses.erase(it); + updated = true; + if (v4only) break; // otherwise might be more than one address + } else + ++it; + } + if (updated) + m_RouterInfo.UpdateSupportedTransports(); + } - void RouterContext::RemoveSSUAddress () - { - bool updated = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto it = addresses.begin (); it != addresses.end ();) - { - if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU) - { - it = addresses.erase (it); - updated = true; - } - else - ++it; - } - if (updated) - m_RouterInfo.UpdateSupportedTransports (); - } - - void RouterContext::SetUnreachableSSU2 (bool v4, bool v6) - { - if (IsSSU2Only ()) - SetUnreachable (v4, v6); - } + void RouterContext::RemoveSSUAddress() { + bool updated = false; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto it = addresses.begin(); it != addresses.end();) { + if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU) { + it = addresses.erase(it); + updated = true; + } else + ++it; + } + if (updated) + m_RouterInfo.UpdateSupportedTransports(); + } - void RouterContext::SetUnreachable (bool v4, bool v6) - { - if (v4 || (v6 && !SupportsV4 ())) - { - // set caps - uint8_t caps = m_RouterInfo.GetCaps (); - caps &= ~i2p::data::RouterInfo::eReachable; - caps |= i2p::data::RouterInfo::eUnreachable; - if (v6 || !SupportsV6 ()) - caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill - m_RouterInfo.UpdateCaps (caps); - } - uint16_t port = 0; - // delete previous introducers - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr : addresses) - if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) && - ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) - { - addr->published = false; - addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer - addr->ssu->introducers.clear (); - port = addr->port; - } - // unpublish NTCP2 addreeses - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - if (ntcp2) - PublishNTCP2Address (port, false, v4, v6, false); - // update - m_RouterInfo.UpdateSupportedTransports (); - UpdateRouterInfo (); - } + void RouterContext::SetUnreachableSSU2(bool v4, bool v6) { + if (IsSSU2Only()) + SetUnreachable(v4, v6); + } - void RouterContext::SetReachable (bool v4, bool v6) - { - if (v4 || (v6 && !SupportsV4 ())) - { - // update caps - uint8_t caps = m_RouterInfo.GetCaps (); - caps &= ~i2p::data::RouterInfo::eUnreachable; - caps |= i2p::data::RouterInfo::eReachable; - if (m_IsFloodfill) - caps |= i2p::data::RouterInfo::eFloodfill; - m_RouterInfo.UpdateCaps (caps); - } - uint16_t port = 0; - // delete previous introducers - bool isSSU2Published = IsSSU2Only (); // TODO - if (isSSU2Published) - i2p::config::GetOption ("ssu2.published", isSSU2Published); - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr : addresses) - if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) && - ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) - { - addr->published = true; - addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; - addr->ssu->introducers.clear (); - if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ())) - port = addr->port; - } - // publish NTCP2 - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - if (ntcp2) - { - bool published; i2p::config::GetOption ("ntcp2.published", published); - if (published) - { - uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); - if (!ntcp2Port) ntcp2Port = port; - PublishNTCP2Address (ntcp2Port, true, v4, v6, false); - } - } - // update - m_RouterInfo.UpdateSupportedTransports (); - UpdateRouterInfo (); - } + void RouterContext::SetUnreachable(bool v4, bool v6) { + if (v4 || (v6 && !SupportsV4())) { + // set caps + uint8_t caps = m_RouterInfo.GetCaps(); + caps &= ~i2p::data::RouterInfo::eReachable; + caps |= i2p::data::RouterInfo::eUnreachable; + if (v6 || !SupportsV6()) + caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill + m_RouterInfo.UpdateCaps(caps); + } + uint16_t port = 0; + // delete previous introducers + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) + if (addr->ssu && (!addr->IsSSU2() || IsSSU2Only()) && + ((v4 && addr->IsV4()) || (v6 && addr->IsV6()))) { + addr->published = false; + addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer + addr->ssu->introducers.clear(); + port = addr->port; + } + // unpublish NTCP2 addreeses + bool ntcp2; + i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) + PublishNTCP2Address(port, false, v4, v6, false); + // update + m_RouterInfo.UpdateSupportedTransports(); + UpdateRouterInfo(); + } - void RouterContext::SetSupportsV6 (bool supportsV6) - { - if (supportsV6) - { - // insert v6 addresses if necessary - bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; - uint16_t port = 0; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr: addresses) - { - if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host)) - { - switch (addr->transportStyle) - { - case i2p::data::RouterInfo::eTransportSSU: - foundSSU = true; - break; - case i2p::data::RouterInfo::eTransportNTCP: - foundNTCP2 = true; - break; - case i2p::data::RouterInfo::eTransportSSU2: - foundSSU2 = true; - break; - default: ; - } - } - port = addr->port; - } - if (!port) - { - i2p::config::GetOption("port", port); - if (!port) port = SelectRandomPort (); - } - // SSU - bool ssu; i2p::config::GetOption("ssu", ssu); - if (!foundSSU && ssu) - { - std::string host = "::1"; // TODO: read host - m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); - } - // NTCP2 - if (!foundNTCP2) - { - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); - if (ntcp2) - { - if (ntcp2Published) - { - std::string ntcp2Host; - if (!i2p::config::IsDefault ("ntcp2.addressv6")) - i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); - else - ntcp2Host = "::1"; - uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); - if (!ntcp2Port) ntcp2Port = port; - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); - } - else - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6); - } - } - // SSU2 - if (!foundSSU2) - { - bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - if (ssu2) - { - bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published); - if (ssu2Published) - { - uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; - m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port); - } - else - m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6); - } - } - m_RouterInfo.EnableV6 (); - } - else - m_RouterInfo.DisableV6 (); - UpdateRouterInfo (); - } + void RouterContext::SetReachable(bool v4, bool v6) { + if (v4 || (v6 && !SupportsV4())) { + // update caps + uint8_t caps = m_RouterInfo.GetCaps(); + caps &= ~i2p::data::RouterInfo::eUnreachable; + caps |= i2p::data::RouterInfo::eReachable; + if (m_IsFloodfill) + caps |= i2p::data::RouterInfo::eFloodfill; + m_RouterInfo.UpdateCaps(caps); + } + uint16_t port = 0; + // delete previous introducers + bool isSSU2Published = IsSSU2Only(); // TODO + if (isSSU2Published) + i2p::config::GetOption("ssu2.published", isSSU2Published); + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) + if (addr->ssu && (!addr->IsSSU2() || isSSU2Published) && + ((v4 && addr->IsV4()) || (v6 && addr->IsV6()))) { + addr->published = true; + addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; + addr->ssu->introducers.clear(); + if (addr->port && (!addr->IsSSU2() || IsSSU2Only())) + port = addr->port; + } + // publish NTCP2 + bool ntcp2; + i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) { + bool published; + i2p::config::GetOption("ntcp2.published", published); + if (published) { + uint16_t ntcp2Port; + i2p::config::GetOption("ntcp2.port", ntcp2Port); + if (!ntcp2Port) ntcp2Port = port; + PublishNTCP2Address(ntcp2Port, true, v4, v6, false); + } + } + // update + m_RouterInfo.UpdateSupportedTransports(); + UpdateRouterInfo(); + } - void RouterContext::SetSupportsV4 (bool supportsV4) - { - // check if updates - if (supportsV4 && SupportsV4 ()) return; - if (!supportsV4 && !SupportsV4 ()) return; - // update - if (supportsV4) - { - bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; - std::string host = "127.0.0.1"; - uint16_t port = 0; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr: addresses) - { - if (addr->IsV4 ()) - { - switch (addr->transportStyle) - { - case i2p::data::RouterInfo::eTransportSSU: - foundSSU = true; - break; - case i2p::data::RouterInfo::eTransportNTCP: - foundNTCP2 = true; - break; - case i2p::data::RouterInfo::eTransportSSU2: - foundSSU2 = true; - break; - default: ; - } - } - if (addr->port) port = addr->port; - } - if (!port) - { - i2p::config::GetOption("port", port); - if (!port) port = SelectRandomPort (); - } - // SSU - bool ssu; i2p::config::GetOption("ssu", ssu); - if (!foundSSU && ssu) - m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); + void RouterContext::SetSupportsV6(bool supportsV6) { + if (supportsV6) { + // insert v6 addresses if necessary + bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; + uint16_t port = 0; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) { + if (addr->IsV6() && !i2p::util::net::IsYggdrasilAddress(addr->host)) { + switch (addr->transportStyle) { + case i2p::data::RouterInfo::eTransportSSU: + foundSSU = true; + break; + case i2p::data::RouterInfo::eTransportNTCP: + foundNTCP2 = true; + break; + case i2p::data::RouterInfo::eTransportSSU2: + foundSSU2 = true; + break; + default:; + } + } + port = addr->port; + } + if (!port) { + i2p::config::GetOption("port", port); + if (!port) port = SelectRandomPort(); + } + // SSU + bool ssu; + i2p::config::GetOption("ssu", ssu); + if (!foundSSU && ssu) { + std::string host = "::1"; // TODO: read host + m_RouterInfo.AddSSUAddress(host.c_str(), port, nullptr); + } + // NTCP2 + if (!foundNTCP2) { + bool ntcp2; + i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ntcp2Published; + i2p::config::GetOption("ntcp2.published", ntcp2Published); + if (ntcp2) { + if (ntcp2Published) { + std::string ntcp2Host; + if (!i2p::config::IsDefault("ntcp2.addressv6")) + i2p::config::GetOption("ntcp2.addressv6", ntcp2Host); + else + ntcp2Host = "::1"; + uint16_t ntcp2Port; + i2p::config::GetOption("ntcp2.port", ntcp2Port); + if (!ntcp2Port) ntcp2Port = port; + m_RouterInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, + boost::asio::ip::address::from_string(ntcp2Host), ntcp2Port); + } else + m_RouterInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, + boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6); + } + } + // SSU2 + if (!foundSSU2) { + bool ssu2; + i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) { + bool ssu2Published; + i2p::config::GetOption("ssu2.published", ssu2Published); + if (ssu2Published) { + uint16_t ssu2Port; + i2p::config::GetOption("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + boost::asio::ip::address::from_string("::1"), ssu2Port); + } else + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + i2p::data::RouterInfo::eV6); + } + } + m_RouterInfo.EnableV6(); + } else + m_RouterInfo.DisableV6(); + UpdateRouterInfo(); + } - // NTCP2 - if (!foundNTCP2) - { - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - if (ntcp2) - { - bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); - if (ntcp2Published) - { - uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); - if (!ntcp2Port) ntcp2Port = port; - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port); - } - else - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4); - } - } - // SSU2 - if (!foundSSU2) - { - bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - if (ssu2) - { - bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published); - if (ssu2Published) - { - uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; - m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port); - } - else - m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV4); - } - } - m_RouterInfo.EnableV4 (); - } - else - m_RouterInfo.DisableV4 (); - UpdateRouterInfo (); - } + void RouterContext::SetSupportsV4(bool supportsV4) { + // check if updates + if (supportsV4 && SupportsV4()) return; + if (!supportsV4 && !SupportsV4()) return; + // update + if (supportsV4) { + bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; + std::string host = "127.0.0.1"; + uint16_t port = 0; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) { + if (addr->IsV4()) { + switch (addr->transportStyle) { + case i2p::data::RouterInfo::eTransportSSU: + foundSSU = true; + break; + case i2p::data::RouterInfo::eTransportNTCP: + foundNTCP2 = true; + break; + case i2p::data::RouterInfo::eTransportSSU2: + foundSSU2 = true; + break; + default:; + } + } + if (addr->port) port = addr->port; + } + if (!port) { + i2p::config::GetOption("port", port); + if (!port) port = SelectRandomPort(); + } + // SSU + bool ssu; + i2p::config::GetOption("ssu", ssu); + if (!foundSSU && ssu) + m_RouterInfo.AddSSUAddress(host.c_str(), port, nullptr); - void RouterContext::SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host) - { - if (supportsmesh) - { - m_RouterInfo.EnableMesh (); - uint16_t port = 0; - i2p::config::GetOption ("ntcp2.port", port); - if (!port) i2p::config::GetOption("port", port); - bool foundMesh = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr: addresses) - { - if (!port) port = addr->port; - if (i2p::util::net::IsYggdrasilAddress (addr->host)) - { - foundMesh = true; - break; - } - } - if (!foundMesh) - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port); - } - else - m_RouterInfo.DisableMesh (); - UpdateRouterInfo (); - } + // NTCP2 + if (!foundNTCP2) { + bool ntcp2; + i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) { + bool ntcp2Published; + i2p::config::GetOption("ntcp2.published", ntcp2Published); + if (ntcp2Published) { + uint16_t ntcp2Port; + i2p::config::GetOption("ntcp2.port", ntcp2Port); + if (!ntcp2Port) ntcp2Port = port; + m_RouterInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, + boost::asio::ip::address::from_string(host), ntcp2Port); + } else + m_RouterInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, + boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4); + } + } + // SSU2 + if (!foundSSU2) { + bool ssu2; + i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) { + bool ssu2Published; + i2p::config::GetOption("ssu2.published", ssu2Published); + if (ssu2Published) { + uint16_t ssu2Port; + i2p::config::GetOption("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + boost::asio::ip::address::from_string("127.0.0.1"), ssu2Port); + } else + m_RouterInfo.AddSSU2Address(m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, + i2p::data::RouterInfo::eV4); + } + } + m_RouterInfo.EnableV4(); + } else + m_RouterInfo.DisableV4(); + UpdateRouterInfo(); + } - void RouterContext::SetMTU (int mtu, bool v4) - { - if (mtu < 1280 || mtu > 1500) return; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr: addresses) - { - if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) - { - if (!addr->IsSSU2 ()) // SSU1 - { - // round to multiple of 16 - if (v4) - { - if (mtu > 1484) mtu = 1484; - else - { - mtu -= 12; - mtu = (mtu >> 4) << 4; - mtu += 12; - } - } - else - { - if (mtu > 1488) mtu = 1488; - else - mtu = (mtu >> 4) << 4; - } - } - if (mtu) - { - addr->ssu->mtu = mtu; - LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu); - } - } - } - } + void RouterContext::SetSupportsMesh(bool supportsmesh, const boost::asio::ip::address_v6 &host) { + if (supportsmesh) { + m_RouterInfo.EnableMesh(); + uint16_t port = 0; + i2p::config::GetOption("ntcp2.port", port); + if (!port) i2p::config::GetOption("port", port); + bool foundMesh = false; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) { + if (!port) port = addr->port; + if (i2p::util::net::IsYggdrasilAddress(addr->host)) { + foundMesh = true; + break; + } + } + if (!foundMesh) + m_RouterInfo.AddNTCP2Address(m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port); + } else + m_RouterInfo.DisableMesh(); + UpdateRouterInfo(); + } - void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host) - { - bool isYgg = i2p::util::net::IsYggdrasilAddress (host); - bool updated = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr: addresses) - { - if (addr->IsPublishedNTCP2 ()) - { - bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host); - if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1))) - { - if (addr->host != host) - { - addr->host = host; - updated = true; - } - break; - } - } - } + void RouterContext::SetMTU(int mtu, bool v4) { + if (mtu < 1280 || mtu > 1500) return; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) { + if (addr->ssu && ((v4 && addr->IsV4()) || (!v4 && addr->IsV6()))) { + if (!addr->IsSSU2()) // SSU1 + { + // round to multiple of 16 + if (v4) { + if (mtu > 1484) mtu = 1484; + else { + mtu -= 12; + mtu = (mtu >> 4) << 4; + mtu += 12; + } + } else { + if (mtu > 1488) mtu = 1488; + else + mtu = (mtu >> 4) << 4; + } + } + if (mtu) { + addr->ssu->mtu = mtu; + LogPrint(eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), + " is set to ", mtu); + } + } + } + } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::UpdateNTCP2V6Address(const boost::asio::ip::address &host) { + bool isYgg = i2p::util::net::IsYggdrasilAddress(host); + bool updated = false; + auto &addresses = m_RouterInfo.GetAddresses(); + for (auto &addr: addresses) { + if (addr->IsPublishedNTCP2()) { + bool isYgg1 = i2p::util::net::IsYggdrasilAddress(addr->host); + if (addr->IsV6() && ((isYgg && isYgg1) || (!isYgg && !isYgg1))) { + if (addr->host != host) { + addr->host = host; + updated = true; + } + break; + } + } + } - void RouterContext::UpdateStats () - { - if (m_IsFloodfill) - { - // update routers and leasesets - m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, std::to_string(i2p::data::netdb.GetNumLeaseSets ())); - m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, std::to_string(i2p::data::netdb.GetNumRouters ())); - UpdateRouterInfo (); - } - } + if (updated) + UpdateRouterInfo(); + } - void RouterContext::UpdateTimestamp (uint64_t ts) - { - if (ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) - UpdateRouterInfo (); - } + void RouterContext::UpdateStats() { + if (m_IsFloodfill) { + // update routers and leasesets + m_RouterInfo.SetProperty(i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, + std::to_string(i2p::data::netdb.GetNumLeaseSets())); + m_RouterInfo.SetProperty(i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, + std::to_string(i2p::data::netdb.GetNumRouters())); + UpdateRouterInfo(); + } + } - bool RouterContext::Load () - { - { - std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary); - if (!fk.is_open ()) return false; - fk.seekg (0, std::ios::end); - size_t len = fk.tellg(); - fk.seekg (0, std::ios::beg); + void RouterContext::UpdateTimestamp(uint64_t ts) { + if (ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) + UpdateRouterInfo(); + } - if (len == sizeof (i2p::data::Keys)) // old keys file format - { - i2p::data::Keys keys; - fk.read ((char *)&keys, sizeof (keys)); - m_Keys = keys; - } - else // new keys file format - { - uint8_t * buf = new uint8_t[len]; - fk.read ((char *)buf, len); - m_Keys.FromBuffer (buf, len); - delete[] buf; - } - } - std::shared_ptr oldIdentity; - if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1 || - m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) - { - // update keys - LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); - oldIdentity = m_Keys.GetPublic (); - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); - SaveKeys (); - } - // read NTCP2 keys if available - std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); - if (n2k) - { - n2k.seekg (0, std::ios::end); - size_t len = n2k.tellg(); - n2k.seekg (0, std::ios::beg); - if (len == sizeof (NTCP2PrivateKeys)) - { - m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); - n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); - } - n2k.close (); - } - // read RouterInfo - m_RouterInfo.SetRouterIdentity (oldIdentity ? oldIdentity : GetIdentity ()); - i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); - if (!routerInfo.IsUnreachable ()) // router.info looks good - { - m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); - if (oldIdentity) - m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys - m_RouterInfo.SetProperty ("router.version", I2P_VERSION); - m_RouterInfo.DeleteProperty ("coreVersion"); // TODO: remove later - } - else - { - LogPrint (eLogError, ROUTER_INFO, " is malformed. Creating new"); - NewRouterInfo (); - } + bool RouterContext::Load() { + { + std::ifstream fk(i2p::fs::DataDirPath(ROUTER_KEYS), std::ifstream::in | std::ifstream::binary); + if (!fk.is_open()) return false; + fk.seekg(0, std::ios::end); + size_t len = fk.tellg(); + fk.seekg(0, std::ios::beg); - if (IsUnreachable ()) - SetReachable (true, true); // we assume reachable until we discover firewall through peer tests + if (len == sizeof(i2p::data::Keys)) // old keys file format + { + i2p::data::Keys keys; + fk.read((char *) &keys, sizeof(keys)); + m_Keys = keys; + } else // new keys file format + { + uint8_t *buf = new uint8_t[len]; + fk.read((char *) buf, len); + m_Keys.FromBuffer(buf, len); + delete[] buf; + } + } + std::shared_ptr oldIdentity; + if (m_Keys.GetPublic()->GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1 || + m_Keys.GetPublic()->GetCryptoKeyType() == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) { + // update keys + LogPrint(eLogInfo, "Router: router keys are obsolete. Creating new"); + oldIdentity = m_Keys.GetPublic(); + m_Keys = i2p::data::PrivateKeys::CreateRandomKeys(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); + SaveKeys(); + } + // read NTCP2 keys if available + std::ifstream n2k(i2p::fs::DataDirPath(NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); + if (n2k) { + n2k.seekg(0, std::ios::end); + size_t len = n2k.tellg(); + n2k.seekg(0, std::ios::beg); + if (len == sizeof(NTCP2PrivateKeys)) { + m_NTCP2Keys.reset(new NTCP2PrivateKeys()); + n2k.read((char *) m_NTCP2Keys.get(), sizeof(NTCP2PrivateKeys)); + } + n2k.close(); + } + // read RouterInfo + m_RouterInfo.SetRouterIdentity(oldIdentity ? oldIdentity : GetIdentity()); + i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath(ROUTER_INFO)); + if (!routerInfo.IsUnreachable()) // router.info looks good + { + m_RouterInfo.Update(routerInfo.GetBuffer(), routerInfo.GetBufferLen()); + if (oldIdentity) + m_RouterInfo.SetRouterIdentity(GetIdentity()); // from new keys + m_RouterInfo.SetProperty("router.version", I2P_VERSION); + m_RouterInfo.DeleteProperty("coreVersion"); // TODO: remove later + } else { + LogPrint(eLogError, ROUTER_INFO, " is malformed. Creating new"); + NewRouterInfo(); + } - // read NTCP2 - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); - if (ntcp2 || ygg) - { - if (!m_NTCP2Keys) NewNTCP2Keys (); - UpdateNTCP2Address (true); // enable NTCP2 - } - else - UpdateNTCP2Address (false); // disable NTCP2 + if (IsUnreachable()) + SetReachable(true, true); // we assume reachable until we discover firewall through peer tests - // read SSU2 - bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - if (ssu2) - { - // read SSU2 keys if available - std::ifstream s2k (i2p::fs::DataDirPath (SSU2_KEYS), std::ifstream::in | std::ifstream::binary); - if (s2k) - { - s2k.seekg (0, std::ios::end); - size_t len = s2k.tellg(); - s2k.seekg (0, std::ios::beg); - if (len == sizeof (SSU2PrivateKeys)) - { - m_SSU2Keys.reset (new SSU2PrivateKeys ()); - s2k.read ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); - } - s2k.close (); - } - if (!m_SSU2Keys) NewSSU2Keys (); - UpdateSSU2Address (true); // enable SSU2 - } - else - UpdateSSU2Address (false); // disable SSU2 + // read NTCP2 + bool ntcp2; + i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ygg; + i2p::config::GetOption("meshnets.yggdrasil", ygg); + if (ntcp2 || ygg) { + if (!m_NTCP2Keys) NewNTCP2Keys(); + UpdateNTCP2Address(true); // enable NTCP2 + } else + UpdateNTCP2Address(false); // disable NTCP2 - return true; - } + // read SSU2 + bool ssu2; + i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) { + // read SSU2 keys if available + std::ifstream s2k(i2p::fs::DataDirPath(SSU2_KEYS), std::ifstream::in | std::ifstream::binary); + if (s2k) { + s2k.seekg(0, std::ios::end); + size_t len = s2k.tellg(); + s2k.seekg(0, std::ios::beg); + if (len == sizeof(SSU2PrivateKeys)) { + m_SSU2Keys.reset(new SSU2PrivateKeys()); + s2k.read((char *) m_SSU2Keys.get(), sizeof(SSU2PrivateKeys)); + } + s2k.close(); + } + if (!m_SSU2Keys) NewSSU2Keys(); + UpdateSSU2Address(true); // enable SSU2 + } else + UpdateSSU2Address(false); // disable SSU2 - void RouterContext::SaveKeys () - { - // save in the same format as .dat files - std::ofstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ofstream::binary | std::ofstream::out); - size_t len = m_Keys.GetFullLen (); - uint8_t * buf = new uint8_t[len]; - m_Keys.ToBuffer (buf, len); - fk.write ((char *)buf, len); - delete[] buf; - } + return true; + } - std::shared_ptr RouterContext::GetTunnelPool () const - { - return i2p::tunnel::tunnels.GetExploratoryPool (); - } + void RouterContext::SaveKeys() { + // save in the same format as .dat files + std::ofstream fk(i2p::fs::DataDirPath(ROUTER_KEYS), std::ofstream::binary | std::ofstream::out); + size_t len = m_Keys.GetFullLen(); + uint8_t *buf = new uint8_t[len]; + m_Keys.ToBuffer(buf, len); + fk.write((char *) buf, len); + delete[] buf; + } - void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len) - { - i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); - } + std::shared_ptr RouterContext::GetTunnelPool() const { + return i2p::tunnel::tunnels.GetExploratoryPool(); + } - bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) - { - auto msg = CreateI2NPMessage (typeID, payload, len, msgID); - if (!msg) return false; - i2p::HandleI2NPMessage (msg); - return true; - } + void RouterContext::HandleI2NPMessage(const uint8_t *buf, size_t len) { + i2p::HandleI2NPMessage(CreateI2NPMessage(buf, GetI2NPMessageLength(buf, len))); + } + + bool + RouterContext::HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID) { + auto msg = CreateI2NPMessage(typeID, payload, len, msgID); + if (!msg) return false; + i2p::HandleI2NPMessage(msg); + return true; + } - void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) - { - std::unique_lock l(m_GarlicMutex); - uint8_t * buf = msg->GetPayload (); - uint32_t len = bufbe32toh (buf); - if (len > msg->GetLength ()) - { - LogPrint (eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", msg->GetLength ()); - return; - } - buf += 4; - if (!HandleECIESx25519TagMessage (buf, len)) // try tag first - { - // then Noise_N one-time decryption - if (m_ECIESSession) - m_ECIESSession->HandleNextMessage (buf, len); - else - LogPrint (eLogError, "Router: Session is not set for ECIES router"); - } - } + void RouterContext::ProcessGarlicMessage(std::shared_ptr msg) { + std::unique_lock l(m_GarlicMutex); + uint8_t *buf = msg->GetPayload(); + uint32_t len = bufbe32toh(buf); + if (len > msg->GetLength()) { + LogPrint(eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", + msg->GetLength()); + return; + } + buf += 4; + if (!HandleECIESx25519TagMessage(buf, len)) // try tag first + { + // then Noise_N one-time decryption + if (m_ECIESSession) + m_ECIESSession->HandleNextMessage(buf, len); + else + LogPrint(eLogError, "Router: Session is not set for ECIES router"); + } + } - void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr msg) - { - if (i2p::data::netdb.GetPublishReplyToken () == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET)) - i2p::data::netdb.PostI2NPMsg (msg); - else - { - std::unique_lock l(m_GarlicMutex); - i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); - } - } + void RouterContext::ProcessDeliveryStatusMessage(std::shared_ptr msg) { + if (i2p::data::netdb.GetPublishReplyToken() == bufbe32toh(msg->GetPayload() + DELIVERY_STATUS_MSGID_OFFSET)) + i2p::data::netdb.PostI2NPMsg(msg); + else { + std::unique_lock l(m_GarlicMutex); + i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage(msg); + } + } - void RouterContext::CleanupDestination () - { - std::unique_lock l(m_GarlicMutex); - i2p::garlic::GarlicDestination::CleanupExpiredTags (); - } + void RouterContext::CleanupDestination() { + std::unique_lock l(m_GarlicMutex); + i2p::garlic::GarlicDestination::CleanupExpiredTags(); + } - uint32_t RouterContext::GetUptime () const - { - return std::chrono::duration_cast (std::chrono::steady_clock::now() - m_StartupTime).count (); - } + uint32_t RouterContext::GetUptime() const { + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_StartupTime).count(); + } - bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const - { - return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data) : false; - } + bool + RouterContext::Decrypt(const uint8_t *encrypted, uint8_t *data, i2p::data::CryptoKeyType preferredCrypto) const { + return m_Decryptor ? m_Decryptor->Decrypt(encrypted, data) : false; + } - bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data) - { - return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE); - } + bool RouterContext::DecryptTunnelBuildRecord(const uint8_t *encrypted, uint8_t *data) { + return DecryptECIESTunnelBuildRecord(encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE); + } - bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize) - { - // m_InitialNoiseState is h = SHA256(h || hepk) - m_CurrentNoiseState = m_InitialNoiseState; - m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk) - uint8_t sharedSecret[32]; - if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret)) - { - LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); - return false; - } - m_CurrentNoiseState.MixKey (sharedSecret); - encrypted += 32; - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32, - m_CurrentNoiseState.m_CK + 32, nonce, data, clearTextSize, false)) // decrypt - { - LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed"); - return false; - } - m_CurrentNoiseState.MixHash (encrypted, clearTextSize + 16); // h = SHA256(h || ciphertext) - return true; - } + bool RouterContext::DecryptECIESTunnelBuildRecord(const uint8_t *encrypted, uint8_t *data, size_t clearTextSize) { + // m_InitialNoiseState is h = SHA256(h || hepk) + m_CurrentNoiseState = m_InitialNoiseState; + m_CurrentNoiseState.MixHash(encrypted, 32); // h = SHA256(h || sepk) + uint8_t sharedSecret[32]; + if (!m_TunnelDecryptor->Decrypt(encrypted, sharedSecret)) { + LogPrint(eLogWarning, "Router: Incorrect ephemeral public key"); + return false; + } + m_CurrentNoiseState.MixKey(sharedSecret); + encrypted += 32; + uint8_t nonce[12]; + memset(nonce, 0, 12); + if (!i2p::crypto::AEADChaCha20Poly1305(encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32, + m_CurrentNoiseState.m_CK + 32, nonce, data, clearTextSize, + false)) // decrypt + { + LogPrint(eLogWarning, "Router: Tunnel record AEAD decryption failed"); + return false; + } + m_CurrentNoiseState.MixHash(encrypted, clearTextSize + 16); // h = SHA256(h || ciphertext) + return true; + } - bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data) - { - return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); - } + bool RouterContext::DecryptTunnelShortRequestRecord(const uint8_t *encrypted, uint8_t *data) { + return DecryptECIESTunnelBuildRecord(encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); + } - i2p::crypto::X25519Keys& RouterContext::GetNTCP2StaticKeys () - { - if (!m_NTCP2StaticKeys) - { - if (!m_NTCP2Keys) NewNTCP2Keys (); - auto x = new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey); - if (!m_NTCP2StaticKeys) - m_NTCP2StaticKeys.reset (x); - else - delete x; - } - return *m_NTCP2StaticKeys; - } + i2p::crypto::X25519Keys &RouterContext::GetNTCP2StaticKeys() { + if (!m_NTCP2StaticKeys) { + if (!m_NTCP2Keys) NewNTCP2Keys(); + auto x = new i2p::crypto::X25519Keys(m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey); + if (!m_NTCP2StaticKeys) + m_NTCP2StaticKeys.reset(x); + else + delete x; + } + return *m_NTCP2StaticKeys; + } - i2p::crypto::X25519Keys& RouterContext::GetSSU2StaticKeys () - { - if (!m_SSU2StaticKeys) - { - if (!m_SSU2Keys) NewSSU2Keys (); - auto x = new i2p::crypto::X25519Keys (m_SSU2Keys->staticPrivateKey, m_SSU2Keys->staticPublicKey); - if (!m_SSU2StaticKeys) - m_SSU2StaticKeys.reset (x); - else - delete x; - } - return *m_SSU2StaticKeys; - } + i2p::crypto::X25519Keys &RouterContext::GetSSU2StaticKeys() { + if (!m_SSU2StaticKeys) { + if (!m_SSU2Keys) NewSSU2Keys(); + auto x = new i2p::crypto::X25519Keys(m_SSU2Keys->staticPrivateKey, m_SSU2Keys->staticPublicKey); + if (!m_SSU2StaticKeys) + m_SSU2StaticKeys.reset(x); + else + delete x; + } + return *m_SSU2StaticKeys; + } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index b6d3e7f0..05d30d48 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -19,200 +19,265 @@ #include "RouterInfo.h" #include "Garlic.h" -namespace i2p -{ -namespace garlic -{ - class RouterIncomingRatchetSession; -} +namespace i2p { + namespace garlic { + class RouterIncomingRatchetSession; + } - const char ROUTER_INFO[] = "router.info"; - const char ROUTER_KEYS[] = "router.keys"; - const char NTCP2_KEYS[] = "ntcp2.keys"; - const char SSU2_KEYS[] = "ssu2.keys"; - const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes + const char ROUTER_INFO[] = "router.info"; + const char ROUTER_KEYS[] = "router.keys"; + const char NTCP2_KEYS[] = "ntcp2.keys"; + const char SSU2_KEYS[] = "ssu2.keys"; + const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes - enum RouterStatus - { - eRouterStatusOK = 0, - eRouterStatusTesting = 1, - eRouterStatusFirewalled = 2, - eRouterStatusError = 3, - eRouterStatusUnknown = 4, - eRouterStatusProxy = 5, - eRouterStatusMesh = 6 - }; + enum RouterStatus { + eRouterStatusOK = 0, + eRouterStatusTesting = 1, + eRouterStatusFirewalled = 2, + eRouterStatusError = 3, + eRouterStatusUnknown = 4, + eRouterStatusProxy = 5, + eRouterStatusMesh = 6 + }; - enum RouterError - { - eRouterErrorNone = 0, - eRouterErrorClockSkew = 1, - eRouterErrorOffline = 2, - eRouterErrorSymmetricNAT = 3 - }; + enum RouterError { + eRouterErrorNone = 0, + eRouterErrorClockSkew = 1, + eRouterErrorOffline = 2, + eRouterErrorSymmetricNAT = 3 + }; - class RouterContext: public i2p::garlic::GarlicDestination - { - private: + class RouterContext : public i2p::garlic::GarlicDestination { + private: - struct NTCP2PrivateKeys - { - uint8_t staticPublicKey[32]; - uint8_t staticPrivateKey[32]; - uint8_t iv[16]; - }; + struct NTCP2PrivateKeys { + uint8_t staticPublicKey[32]; + uint8_t staticPrivateKey[32]; + uint8_t iv[16]; + }; - struct SSU2PrivateKeys - { - uint8_t staticPublicKey[32]; - uint8_t staticPrivateKey[32]; - uint8_t intro[32]; - }; + struct SSU2PrivateKeys { + uint8_t staticPublicKey[32]; + uint8_t staticPrivateKey[32]; + uint8_t intro[32]; + }; - public: + public: - RouterContext (); - void Init (); + RouterContext(); - const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - i2p::data::LocalRouterInfo& GetRouterInfo () { return m_RouterInfo; }; - std::shared_ptr GetSharedRouterInfo () - { - return std::shared_ptr (&m_RouterInfo, - [](i2p::data::RouterInfo *) {}); - } - std::shared_ptr GetSharedDestination () - { - return std::shared_ptr (this, - [](i2p::garlic::GarlicDestination *) {}); - } + void Init(); - const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; - const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; - const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; - i2p::crypto::X25519Keys& GetNTCP2StaticKeys (); + const i2p::data::PrivateKeys &GetPrivateKeys() const { return m_Keys; }; - const uint8_t * GetSSU2StaticPublicKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; }; - 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 (); + i2p::data::LocalRouterInfo &GetRouterInfo() { return m_RouterInfo; }; - uint32_t GetUptime () const; // in seconds - uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; - uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; }; - 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); + std::shared_ptr GetSharedRouterInfo() { + return std::shared_ptr(&m_RouterInfo, + [](i2p::data::RouterInfo *) {}); + } - 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; }; + std::shared_ptr GetSharedDestination() { + return std::shared_ptr(this, + [](i2p::garlic::GarlicDestination *) {}); + } - 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 + const uint8_t *GetNTCP2StaticPublicKey() const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; - // implements LocalDestination - std::shared_ptr 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 () {}; + const uint8_t *GetNTCP2StaticPrivateKey() const { + return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; + }; - // implements GarlicDestination - std::shared_ptr GetLeaseSet () { return nullptr; }; - std::shared_ptr GetTunnelPool () const; + const uint8_t *GetNTCP2IV() const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; - // override GarlicDestination - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatusMessage (std::shared_ptr msg); + i2p::crypto::X25519Keys &GetNTCP2StaticKeys(); - protected: + const uint8_t *GetSSU2StaticPublicKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; }; - // 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); + const uint8_t *GetSSU2StaticPrivateKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; }; - private: + const uint8_t *GetSSU2IntroKey() const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; }; - 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; + i2p::crypto::X25519Keys &GetSSU2StaticKeys(); - 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; - i2p::data::PrivateKeys m_Keys; - std::shared_ptr m_Decryptor, m_TunnelDecryptor; - std::shared_ptr m_ECIESSession; - uint64_t m_LastUpdateTime; // in seconds - bool m_AcceptsTunnels, m_IsFloodfill; - std::chrono::time_point 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 m_NTCP2Keys; - std::unique_ptr m_SSU2Keys; - std::unique_ptr m_NTCP2StaticKeys, m_SSU2StaticKeys; - // for ECIESx25519 - i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; - }; + uint64_t GetTransitBandwidthLimit() const { return (m_BandwidthLimit * m_ShareRatio) / 100LL; }; - 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 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 GetLeaseSet() { return nullptr; }; + + std::shared_ptr GetTunnelPool() const; + + // override GarlicDestination + void ProcessGarlicMessage(std::shared_ptr msg); + + void ProcessDeliveryStatusMessage(std::shared_ptr 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 m_Decryptor, m_TunnelDecryptor; + std::shared_ptr m_ECIESSession; + uint64_t m_LastUpdateTime; // in seconds + bool m_AcceptsTunnels, m_IsFloodfill; + std::chrono::time_point 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 m_NTCP2Keys; + std::unique_ptr m_SSU2Keys; + std::unique_ptr m_NTCP2StaticKeys, m_SSU2StaticKeys; + // for ECIESx25519 + i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; + }; + + extern RouterContext context; } #endif diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 7368001d..7a0cdd63 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -12,9 +12,11 @@ #include #include #include + #if (BOOST_VERSION >= 105300) #include #endif + #include "version.h" #include "util.h" #include "Crypto.h" @@ -25,1495 +27,1273 @@ #include "RouterContext.h" #include "RouterInfo.h" -namespace i2p -{ -namespace data -{ - RouterInfo::Buffer::Buffer (const uint8_t * buf, size_t len) - { - if (len > size ()) len = size (); - memcpy (data (), buf, len); - } +namespace i2p { + namespace data { + RouterInfo::Buffer::Buffer(const uint8_t *buf, size_t len) { + if (len > size()) len = size(); + memcpy(data(), buf, len); + } - RouterInfo::RouterInfo (): m_Buffer (nullptr) - { - m_Addresses = boost::make_shared(); // create empty list - } + RouterInfo::RouterInfo() : m_Buffer(nullptr) { + m_Addresses = boost::make_shared(); // create empty list + } - RouterInfo::RouterInfo (const std::string& fullPath): - m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), - m_SupportedTransports (0),m_ReachableTransports (0), - m_Caps (0), m_Version (0) - { - m_Addresses = boost::make_shared(); // create empty list - m_Buffer = NewBuffer (); // always RouterInfo's - ReadFromFile (fullPath); - } + RouterInfo::RouterInfo(const std::string &fullPath) : + m_FamilyID(0), m_IsUpdated(false), m_IsUnreachable(false), + m_SupportedTransports(0), m_ReachableTransports(0), + m_Caps(0), m_Version(0) { + m_Addresses = boost::make_shared(); // create empty list + m_Buffer = NewBuffer(); // always RouterInfo's + ReadFromFile(fullPath); + } - RouterInfo::RouterInfo (std::shared_ptr&& buf, size_t len): - m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), - m_SupportedTransports (0), m_ReachableTransports (0), - m_Caps (0), m_Version (0) - { - if (len <= MAX_RI_BUFFER_SIZE) - { - m_Addresses = boost::make_shared(); // create empty list - m_Buffer = buf; - m_BufferLen = len; - ReadFromBuffer (true); - } - else - { - LogPrint (eLogError, "RouterInfo: Buffer is too long ", len, ". Ignored"); - m_Buffer = nullptr; - m_IsUnreachable = true; - } - } + RouterInfo::RouterInfo(std::shared_ptr &&buf, size_t len) : + m_FamilyID(0), m_IsUpdated(true), m_IsUnreachable(false), + m_SupportedTransports(0), m_ReachableTransports(0), + m_Caps(0), m_Version(0) { + if (len <= MAX_RI_BUFFER_SIZE) { + m_Addresses = boost::make_shared(); // create empty list + m_Buffer = buf; + m_BufferLen = len; + ReadFromBuffer(true); + } else { + LogPrint(eLogError, "RouterInfo: Buffer is too long ", len, ". Ignored"); + m_Buffer = nullptr; + m_IsUnreachable = true; + } + } - RouterInfo::RouterInfo (const uint8_t * buf, size_t len): - RouterInfo (std::make_shared (buf, len), len) - { - } + RouterInfo::RouterInfo(const uint8_t *buf, size_t len) : + RouterInfo(std::make_shared(buf, len), len) { + } - RouterInfo::~RouterInfo () - { - } + RouterInfo::~RouterInfo() { + } - void RouterInfo::Update (const uint8_t * buf, size_t len) - { - if (len > MAX_RI_BUFFER_SIZE) - { - LogPrint (eLogError, "RouterInfo: Buffer is too long ", len); - m_IsUnreachable = true; - return; - } - // verify signature since we have identity already - int l = len - m_RouterIdentity->GetSignatureLen (); - if (m_RouterIdentity->Verify (buf, l, buf + l)) - { - // clean up - m_IsUpdated = true; - m_IsUnreachable = false; - m_SupportedTransports = 0; - m_ReachableTransports = 0; - m_Caps = 0; - // don't clean up m_Addresses, it will be replaced in ReadFromStream - ClearProperties (); - // copy buffer - UpdateBuffer (buf, len); - // skip identity - size_t identityLen = m_RouterIdentity->GetFullLen (); - // read new RI - std::stringstream str (std::string ((char *)m_Buffer->data () + identityLen, m_BufferLen - identityLen)); - ReadFromStream (str); - // don't delete buffer until saved to the file - } - else - { - LogPrint (eLogError, "RouterInfo: Signature verification failed"); - m_IsUnreachable = true; - } - } + void RouterInfo::Update(const uint8_t *buf, size_t len) { + if (len > MAX_RI_BUFFER_SIZE) { + LogPrint(eLogError, "RouterInfo: Buffer is too long ", len); + m_IsUnreachable = true; + return; + } + // verify signature since we have identity already + int l = len - m_RouterIdentity->GetSignatureLen(); + if (m_RouterIdentity->Verify(buf, l, buf + l)) { + // clean up + m_IsUpdated = true; + m_IsUnreachable = false; + m_SupportedTransports = 0; + m_ReachableTransports = 0; + m_Caps = 0; + // don't clean up m_Addresses, it will be replaced in ReadFromStream + ClearProperties(); + // copy buffer + UpdateBuffer(buf, len); + // skip identity + size_t identityLen = m_RouterIdentity->GetFullLen(); + // read new RI + std::stringstream str(std::string ((char *)m_Buffer->data() + identityLen, m_BufferLen - identityLen)); + ReadFromStream(str); + // don't delete buffer until saved to the file + } else { + LogPrint(eLogError, "RouterInfo: Signature verification failed"); + m_IsUnreachable = true; + } + } - void RouterInfo::SetRouterIdentity (std::shared_ptr identity) - { - m_RouterIdentity = identity; - m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); - } + void RouterInfo::SetRouterIdentity(std::shared_ptr identity) { + m_RouterIdentity = identity; + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch(); + } - bool RouterInfo::LoadFile (const std::string& fullPath) - { - std::ifstream s(fullPath, std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0,std::ios::end); - m_BufferLen = s.tellg (); - if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE) - { - LogPrint(eLogError, "RouterInfo: File", fullPath, " is malformed"); - return false; - } - s.seekg(0, std::ios::beg); - if (!m_Buffer) - m_Buffer = NewBuffer (); - s.read((char *)m_Buffer->data (), m_BufferLen); - } - else - { - LogPrint (eLogError, "RouterInfo: Can't open file ", fullPath); - return false; - } - return true; - } + bool RouterInfo::LoadFile(const std::string &fullPath) { + std::ifstream s(fullPath, std::ifstream::binary); + if (s.is_open()) { + s.seekg(0, std::ios::end); + m_BufferLen = s.tellg(); + if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE) { + LogPrint(eLogError, "RouterInfo: File", fullPath, " is malformed"); + return false; + } + s.seekg(0, std::ios::beg); + if (!m_Buffer) + m_Buffer = NewBuffer(); + s.read((char *) m_Buffer->data(), m_BufferLen); + } else { + LogPrint(eLogError, "RouterInfo: Can't open file ", fullPath); + return false; + } + return true; + } - void RouterInfo::ReadFromFile (const std::string& fullPath) - { - if (LoadFile (fullPath)) - ReadFromBuffer (false); - else - m_IsUnreachable = true; - } + void RouterInfo::ReadFromFile(const std::string &fullPath) { + if (LoadFile(fullPath)) + ReadFromBuffer(false); + else + m_IsUnreachable = true; + } - void RouterInfo::ReadFromBuffer (bool verifySignature) - { - if (!m_Buffer) - { - m_IsUnreachable = true; - return; - } - m_RouterIdentity = std::make_shared(m_Buffer->data (), m_BufferLen); - size_t identityLen = m_RouterIdentity->GetFullLen (); - if (identityLen >= m_BufferLen) - { - LogPrint (eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", m_BufferLen); - m_IsUnreachable = true; - return; - } - if (verifySignature) - { - // reject RSA signatures - if (m_RouterIdentity->IsRSA ()) - { - LogPrint (eLogError, "RouterInfo: RSA signature type is not allowed"); - m_IsUnreachable = true; - return; - } - // verify signature - int l = m_BufferLen - m_RouterIdentity->GetSignatureLen (); - if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer->data (), l, (uint8_t *)m_Buffer->data () + l)) - { - LogPrint (eLogError, "RouterInfo: Signature verification failed"); - m_IsUnreachable = true; - return; - } - m_RouterIdentity->DropVerifier (); - } - // parse RI - std::stringstream str; - str.write ((const char *)m_Buffer->data () + identityLen, m_BufferLen - identityLen); - ReadFromStream (str); - if (!str) - { - LogPrint (eLogError, "RouterInfo: Malformed message"); - m_IsUnreachable = true; - } - } + void RouterInfo::ReadFromBuffer(bool verifySignature) { + if (!m_Buffer) { + m_IsUnreachable = true; + return; + } + m_RouterIdentity = std::make_shared(m_Buffer->data(), m_BufferLen); + size_t identityLen = m_RouterIdentity->GetFullLen(); + if (identityLen >= m_BufferLen) { + LogPrint(eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", m_BufferLen); + m_IsUnreachable = true; + return; + } + if (verifySignature) { + // reject RSA signatures + if (m_RouterIdentity->IsRSA()) { + LogPrint(eLogError, "RouterInfo: RSA signature type is not allowed"); + m_IsUnreachable = true; + return; + } + // verify signature + int l = m_BufferLen - m_RouterIdentity->GetSignatureLen(); + if (l < 0 || + !m_RouterIdentity->Verify((uint8_t *) m_Buffer->data(), l, (uint8_t *) m_Buffer->data() + l)) { + LogPrint(eLogError, "RouterInfo: Signature verification failed"); + m_IsUnreachable = true; + return; + } + m_RouterIdentity->DropVerifier(); + } + // parse RI + std::stringstream str; + str.write((const char *) m_Buffer->data() + identityLen, m_BufferLen - identityLen); + ReadFromStream(str); + if (!str) { + LogPrint(eLogError, "RouterInfo: Malformed message"); + m_IsUnreachable = true; + } + } - void RouterInfo::ReadFromStream (std::istream& s) - { - if (!s) return; - m_Caps = 0; - s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); - m_Timestamp = be64toh (m_Timestamp); - // read addresses - auto addresses = boost::make_shared(); - uint8_t numAddresses; - s.read ((char *)&numAddresses, sizeof (numAddresses)); - addresses->reserve (numAddresses); - for (int i = 0; i < numAddresses; i++) - { - uint8_t supportedTransports = 0; - auto address = std::make_shared
(); - uint8_t cost; // ignore - s.read ((char *)&cost, sizeof (cost)); - s.read ((char *)&address->date, sizeof (address->date)); - bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false; - Tag<32> iV2; // for 'i' field in SSU, TODO: remove later - char transportStyle[6]; - ReadString (transportStyle, 6, s); - if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 - address->transportStyle = eTransportNTCP; - else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2 - { - address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU; - address->ssu.reset (new SSUExt ()); - address->ssu->mtu = 0; - } - else - address->transportStyle = eTransportUnknown; - address->caps = 0; - address->port = 0; - uint16_t size, r = 0; - s.read ((char *)&size, sizeof (size)); if (!s) return; - size = be16toh (size); - if (address->transportStyle == eTransportUnknown) - { - // skip unknown address - s.seekg (size, std::ios_base::cur); - if (s) continue; else return; - } - while (r < size) - { - char key[255], value[255]; - r += ReadString (key, 255, s); - s.seekg (1, std::ios_base::cur); r++; // = - r += ReadString (value, 255, s); - s.seekg (1, std::ios_base::cur); r++; // ; - if (!s) return; - if (!strcmp (key, "host")) - { - boost::system::error_code ecode; - address->host = boost::asio::ip::address::from_string (value, ecode); - if (!ecode && !address->host.is_unspecified ()) isHost = true; - } - else if (!strcmp (key, "port")) - address->port = boost::lexical_cast(value); - else if (!strcmp (key, "mtu")) - { - if (address->ssu) - address->ssu->mtu = boost::lexical_cast(value); - else - LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP"); - } - else if (!strcmp (key, "key")) - { - if (address->ssu) - isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32); - else - LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP"); - } - else if (!strcmp (key, "caps")) - address->caps = ExtractAddressCaps (value); - else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key - { - Base64ToByteStream (value, strlen (value), address->s, 32); - isStaticKey = true; - } - else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro - { - if (address->IsNTCP2 ()) - { - Base64ToByteStream (value, strlen (value), address->i, 16); - address->published = true; // presence of "i" means "published" NTCP2 - } - else if (address->IsSSU2 ()) - Base64ToByteStream (value, strlen (value), address->i, 32); - else - Base64ToByteStream (value, strlen (value), iV2, 32); - } - else if (!strcmp (key, "v")) - { - if (!strcmp (value, "2")) - isV2 = true; - else - LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v"); - } - else if (key[0] == 'i') - { - // introducers - if (!address->ssu) - { - LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); - continue; - } - size_t l = strlen(key); - unsigned char index = key[l-1] - '0'; // TODO: - key[l-1] = 0; - if (index > 9) - { - LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); - if (s) continue; else return; - } - if (index >= address->ssu->introducers.size ()) - { - if (address->ssu->introducers.empty ()) // first time - address->ssu->introducers.reserve (3); - address->ssu->introducers.resize (index + 1); - } - Introducer& introducer = address->ssu->introducers.at (index); - if (!strcmp (key, "ihost")) - { - boost::system::error_code ecode; - introducer.iHost = boost::asio::ip::address::from_string (value, ecode); - } - else if (!strcmp (key, "iport")) - introducer.iPort = boost::lexical_cast(value); - else if (!strcmp (key, "itag")) - introducer.iTag = boost::lexical_cast(value); - else if (!strcmp (key, "ikey") || !strcmp (key, "ih")) - Base64ToByteStream (value, strlen (value), introducer.iKey, 32); - else if (!strcmp (key, "iexp")) - introducer.iExp = boost::lexical_cast(value); - } - if (!s) return; - } - if (address->transportStyle == eTransportNTCP) - { - if (isStaticKey) - { - if (isHost) - { - if (address->host.is_v6 ()) - supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6); - else - supportedTransports |= eNTCP2V4; - m_ReachableTransports |= supportedTransports; - } - else if (!address->published) - { - if (address->caps) - { - if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4; - if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6; - } - else - supportedTransports |= eNTCP2V4; // most likely, since we don't have host - } - } - } - else if (address->transportStyle == eTransportSSU) - { - if (isIntroKey) - { - if (isHost) - supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6; - else if (address->caps & AddressCaps::eV6) - { - supportedTransports |= eSSUV6; - if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6 - } - else - supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4 - if (address->ssu && !address->ssu->introducers.empty ()) - { - // exclude invalid introducers - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numValid = 0; - for (auto& it: address->ssu->introducers) - { - if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT; - if (ts <= it.iExp && it.iPort > 0 && - ((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ()))) - numValid++; - else - { - it.iPort = 0; - if (isV2) numValid++; - } - } - if (numValid) - m_ReachableTransports |= supportedTransports; - else - address->ssu->introducers.resize (0); - } - else if (isHost && address->port) - { - address->published = true; - m_ReachableTransports |= supportedTransports; - } - } - } - if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU)) - { - if (address->IsV4 ()) supportedTransports |= eSSU2V4; - if (address->IsV6 ()) supportedTransports |= eSSU2V6; - if (address->port) - { - if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4; - if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6; - } - if (address->transportStyle == eTransportSSU2) - { - if (address->port) address->published = true; - if (address->ssu && !address->ssu->introducers.empty ()) - { - // exclude invalid introducers - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numValid = 0; - for (auto& it: address->ssu->introducers) - { - if (it.iTag && ts <= it.iExp) - numValid++; - else - it.iTag = 0; - } - if (numValid) - m_ReachableTransports |= supportedTransports; - else - address->ssu->introducers.resize (0); - } - } - else - { - // create additional SSU2 address. TODO: remove later - auto ssu2addr = std::make_shared
(); - ssu2addr->transportStyle = eTransportSSU2; - ssu2addr->host = address->host; ssu2addr->port = address->port; - ssu2addr->s = address->s; ssu2addr->i = iV2; - ssu2addr->date = address->date; ssu2addr->caps = address->caps; - ssu2addr->published = address->published; - ssu2addr->ssu.reset (new SSUExt ()); ssu2addr->ssu->mtu = address->ssu->mtu; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (!address->ssu->introducers.empty ()) - { - for (const auto& introducer: address->ssu->introducers) - if (!introducer.iPort && introducer.iHost.is_unspecified () && ts < introducer.iExp) // SSU2 - ssu2addr->ssu->introducers.push_back (introducer); - if (!ssu2addr->ssu->introducers.empty ()) - m_ReachableTransports |= supportedTransports; - } - addresses->push_back(ssu2addr); - } - } - if (supportedTransports) - { - if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates - addresses->push_back(address); - m_SupportedTransports |= supportedTransports; - } - } + void RouterInfo::ReadFromStream(std::istream &s) { + if (!s) return; + m_Caps = 0; + s.read((char *) &m_Timestamp, sizeof(m_Timestamp)); + m_Timestamp = be64toh(m_Timestamp); + // read addresses + auto addresses = boost::make_shared(); + uint8_t numAddresses; + s.read((char *) &numAddresses, sizeof(numAddresses)); + addresses->reserve(numAddresses); + for (int i = 0; i < numAddresses; i++) { + uint8_t supportedTransports = 0; + auto address = std::make_shared
(); + uint8_t cost; // ignore + s.read((char *) &cost, sizeof(cost)); + s.read((char *) &address->date, sizeof(address->date)); + bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false; + Tag<32> iV2; // for 'i' field in SSU, TODO: remove later + char transportStyle[6]; + ReadString(transportStyle, 6, s); + if (!strncmp(transportStyle, "NTCP", 4)) // NTCP or NTCP2 + address->transportStyle = eTransportNTCP; + else if (!strncmp(transportStyle, "SSU", 3)) // SSU or SSU2 + { + address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU; + address->ssu.reset(new SSUExt()); + address->ssu->mtu = 0; + } else + address->transportStyle = eTransportUnknown; + address->caps = 0; + address->port = 0; + uint16_t size, r = 0; + s.read((char *) &size, sizeof(size)); + if (!s) return; + size = be16toh(size); + if (address->transportStyle == eTransportUnknown) { + // skip unknown address + s.seekg(size, std::ios_base::cur); + if (s) continue; else return; + } + while (r < size) { + char key[255], value[255]; + r += ReadString(key, 255, s); + s.seekg(1, std::ios_base::cur); + r++; // = + r += ReadString(value, 255, s); + s.seekg(1, std::ios_base::cur); + r++; // ; + if (!s) return; + if (!strcmp(key, "host")) { + boost::system::error_code ecode; + address->host = boost::asio::ip::address::from_string(value, ecode); + if (!ecode && !address->host.is_unspecified()) isHost = true; + } else if (!strcmp(key, "port")) + address->port = boost::lexical_cast(value); + else if (!strcmp(key, "mtu")) { + if (address->ssu) + address->ssu->mtu = boost::lexical_cast(value); + else + LogPrint(eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP"); + } else if (!strcmp(key, "key")) { + if (address->ssu) + isIntroKey = (Base64ToByteStream(value, strlen(value), address->i, 32) == 32); + else + LogPrint(eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP"); + } else if (!strcmp(key, "caps")) + address->caps = ExtractAddressCaps(value); + else if (!strcmp(key, "s")) // ntcp2 or ssu2 static key + { + Base64ToByteStream(value, strlen(value), address->s, 32); + isStaticKey = true; + } else if (!strcmp(key, "i")) // ntcp2 iv or ssu2 intro + { + if (address->IsNTCP2()) { + Base64ToByteStream(value, strlen(value), address->i, 16); + address->published = true; // presence of "i" means "published" NTCP2 + } else if (address->IsSSU2()) + Base64ToByteStream(value, strlen(value), address->i, 32); + else + Base64ToByteStream(value, strlen(value), iV2, 32); + } else if (!strcmp(key, "v")) { + if (!strcmp(value, "2")) + isV2 = true; + else + LogPrint(eLogWarning, "RouterInfo: Unexpected value ", value, " for v"); + } else if (key[0] == 'i') { + // introducers + if (!address->ssu) { + LogPrint(eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); + continue; + } + size_t l = strlen(key); + unsigned char index = key[l - 1] - '0'; // TODO: + key[l - 1] = 0; + if (index > 9) { + LogPrint(eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); + if (s) continue; else return; + } + if (index >= address->ssu->introducers.size()) { + if (address->ssu->introducers.empty()) // first time + address->ssu->introducers.reserve(3); + address->ssu->introducers.resize(index + 1); + } + Introducer &introducer = address->ssu->introducers.at(index); + if (!strcmp(key, "ihost")) { + boost::system::error_code ecode; + introducer.iHost = boost::asio::ip::address::from_string(value, ecode); + } else if (!strcmp(key, "iport")) + introducer.iPort = boost::lexical_cast(value); + else if (!strcmp(key, "itag")) + introducer.iTag = boost::lexical_cast(value); + else if (!strcmp(key, "ikey") || !strcmp(key, "ih")) + Base64ToByteStream(value, strlen(value), introducer.iKey, 32); + else if (!strcmp(key, "iexp")) + introducer.iExp = boost::lexical_cast(value); + } + if (!s) return; + } + if (address->transportStyle == eTransportNTCP) { + if (isStaticKey) { + if (isHost) { + if (address->host.is_v6()) + supportedTransports |= (i2p::util::net::IsYggdrasilAddress(address->host) ? eNTCP2V6Mesh + : eNTCP2V6); + else + supportedTransports |= eNTCP2V4; + m_ReachableTransports |= supportedTransports; + } else if (!address->published) { + if (address->caps) { + if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4; + if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6; + } else + supportedTransports |= eNTCP2V4; // most likely, since we don't have host + } + } + } else if (address->transportStyle == eTransportSSU) { + if (isIntroKey) { + if (isHost) + supportedTransports |= address->host.is_v4() ? eSSUV4 : eSSUV6; + else if (address->caps & AddressCaps::eV6) { + supportedTransports |= eSSUV6; + if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6 + } else + supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4 + if (address->ssu && !address->ssu->introducers.empty()) { + // exclude invalid introducers + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + int numValid = 0; + for (auto &it: address->ssu->introducers) { + if (!it.iExp) it.iExp = m_Timestamp / 1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT; + if (ts <= it.iExp && it.iPort > 0 && + ((it.iHost.is_v4() && address->IsV4()) || (it.iHost.is_v6() && address->IsV6()))) + numValid++; + else { + it.iPort = 0; + if (isV2) numValid++; + } + } + if (numValid) + m_ReachableTransports |= supportedTransports; + else + address->ssu->introducers.resize(0); + } else if (isHost && address->port) { + address->published = true; + m_ReachableTransports |= supportedTransports; + } + } + } + if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU)) { + if (address->IsV4()) supportedTransports |= eSSU2V4; + if (address->IsV6()) supportedTransports |= eSSU2V6; + if (address->port) { + if (address->host.is_v4()) m_ReachableTransports |= eSSU2V4; + if (address->host.is_v6()) m_ReachableTransports |= eSSU2V6; + } + if (address->transportStyle == eTransportSSU2) { + if (address->port) address->published = true; + if (address->ssu && !address->ssu->introducers.empty()) { + // exclude invalid introducers + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + int numValid = 0; + for (auto &it: address->ssu->introducers) { + if (it.iTag && ts <= it.iExp) + numValid++; + else + it.iTag = 0; + } + if (numValid) + m_ReachableTransports |= supportedTransports; + else + address->ssu->introducers.resize(0); + } + } else { + // create additional SSU2 address. TODO: remove later + auto ssu2addr = std::make_shared
(); + ssu2addr->transportStyle = eTransportSSU2; + ssu2addr->host = address->host; + ssu2addr->port = address->port; + ssu2addr->s = address->s; + ssu2addr->i = iV2; + ssu2addr->date = address->date; + ssu2addr->caps = address->caps; + ssu2addr->published = address->published; + ssu2addr->ssu.reset(new SSUExt()); + ssu2addr->ssu->mtu = address->ssu->mtu; + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + if (!address->ssu->introducers.empty()) { + for (const auto &introducer: address->ssu->introducers) + if (!introducer.iPort && introducer.iHost.is_unspecified() && + ts < introducer.iExp) // SSU2 + ssu2addr->ssu->introducers.push_back(introducer); + if (!ssu2addr->ssu->introducers.empty()) + m_ReachableTransports |= supportedTransports; + } + addresses->push_back(ssu2addr); + } + } + if (supportedTransports) { + if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates + addresses->push_back(address); + m_SupportedTransports |= supportedTransports; + } + } #if (BOOST_VERSION >= 105300) - boost::atomic_store (&m_Addresses, addresses); + boost::atomic_store (&m_Addresses, addresses); #else - m_Addresses = addresses; // race condition + m_Addresses = addresses; // race condition #endif - // read peers - uint8_t numPeers; - s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; - s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers - // read properties - m_Version = 0; - bool isNetId = false; - std::string family; - uint16_t size, r = 0; - s.read ((char *)&size, sizeof (size)); if (!s) return; - size = be16toh (size); - while (r < size) - { - char key[255], value[255]; - r += ReadString (key, 255, s); - s.seekg (1, std::ios_base::cur); r++; // = - r += ReadString (value, 255, s); - s.seekg (1, std::ios_base::cur); r++; // ; - if (!s) return; - SetProperty (key, value); + // read peers + uint8_t numPeers; + s.read((char *) &numPeers, sizeof(numPeers)); + if (!s) return; + s.seekg(numPeers * 32, std::ios_base::cur); // TODO: read peers + // read properties + m_Version = 0; + bool isNetId = false; + std::string family; + uint16_t size, r = 0; + s.read((char *) &size, sizeof(size)); + if (!s) return; + size = be16toh(size); + while (r < size) { + char key[255], value[255]; + r += ReadString(key, 255, s); + s.seekg(1, std::ios_base::cur); + r++; // = + r += ReadString(value, 255, s); + s.seekg(1, std::ios_base::cur); + r++; // ; + if (!s) return; + SetProperty(key, value); - // extract caps - if (!strcmp (key, "caps")) - ExtractCaps (value); - // extract version - else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION)) - { - m_Version = 0; - char * ch = value; - while (*ch) - { - if (*ch >= '0' && *ch <= '9') - { - m_Version *= 10; - m_Version += (*ch - '0'); - } - ch++; - } - } - // check netId - else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID)) - { - isNetId = true; - if (atoi (value) != i2p::context.GetNetID ()) - { - LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); - m_IsUnreachable = true; - } - } - // family - else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY)) - { - family = value; - boost::to_lower (family); - } - else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG)) - { - if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) - m_FamilyID = netdb.GetFamilies ().GetFamilyID (family); - else - LogPrint (eLogWarning, "RouterInfo: Family ", family, " signature verification failed"); - } + // extract caps + if (!strcmp(key, "caps")) + ExtractCaps(value); + // extract version + else if (!strcmp(key, ROUTER_INFO_PROPERTY_VERSION)) { + m_Version = 0; + char *ch = value; + while (*ch) { + if (*ch >= '0' && *ch <= '9') { + m_Version *= 10; + m_Version += (*ch - '0'); + } + ch++; + } + } + // check netId + else if (!strcmp(key, ROUTER_INFO_PROPERTY_NETID)) { + isNetId = true; + if (atoi(value) != i2p::context.GetNetID()) { + LogPrint(eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); + m_IsUnreachable = true; + } + } + // family + else if (!strcmp(key, ROUTER_INFO_PROPERTY_FAMILY)) { + family = value; + boost::to_lower(family); + } else if (!strcmp(key, ROUTER_INFO_PROPERTY_FAMILY_SIG)) { + if (netdb.GetFamilies().VerifyFamily(family, GetIdentHash(), value)) + m_FamilyID = netdb.GetFamilies().GetFamilyID(family); + else + LogPrint(eLogWarning, "RouterInfo: Family ", family, " signature verification failed"); + } - if (!s) return; - } + if (!s) return; + } - if (!m_SupportedTransports || !isNetId || !m_Version) - SetUnreachable (true); - } + if (!m_SupportedTransports || !isNetId || !m_Version) + SetUnreachable(true); + } - bool RouterInfo::IsFamily (FamilyID famid) const - { - return m_FamilyID == famid; - } + bool RouterInfo::IsFamily(FamilyID famid) const { + return m_FamilyID == famid; + } - void RouterInfo::ExtractCaps (const char * value) - { - const char * cap = value; - while (*cap) - { - switch (*cap) - { - case CAPS_FLAG_FLOODFILL: - m_Caps |= Caps::eFloodfill; - break; - case CAPS_FLAG_HIGH_BANDWIDTH1: - case CAPS_FLAG_HIGH_BANDWIDTH2: - case CAPS_FLAG_HIGH_BANDWIDTH3: - m_Caps |= Caps::eHighBandwidth; - break; - case CAPS_FLAG_EXTRA_BANDWIDTH1: - case CAPS_FLAG_EXTRA_BANDWIDTH2: - m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth; - break; - case CAPS_FLAG_HIDDEN: - m_Caps |= Caps::eHidden; - break; - case CAPS_FLAG_REACHABLE: - m_Caps |= Caps::eReachable; - break; - case CAPS_FLAG_UNREACHABLE: - m_Caps |= Caps::eUnreachable; - break; - default: ; - } - cap++; - } - } + void RouterInfo::ExtractCaps(const char *value) { + const char *cap = value; + while (*cap) { + switch (*cap) { + case CAPS_FLAG_FLOODFILL: + m_Caps |= Caps::eFloodfill; + break; + case CAPS_FLAG_HIGH_BANDWIDTH1: + case CAPS_FLAG_HIGH_BANDWIDTH2: + case CAPS_FLAG_HIGH_BANDWIDTH3: + m_Caps |= Caps::eHighBandwidth; + break; + case CAPS_FLAG_EXTRA_BANDWIDTH1: + case CAPS_FLAG_EXTRA_BANDWIDTH2: + m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth; + break; + case CAPS_FLAG_HIDDEN: + m_Caps |= Caps::eHidden; + break; + case CAPS_FLAG_REACHABLE: + m_Caps |= Caps::eReachable; + break; + case CAPS_FLAG_UNREACHABLE: + m_Caps |= Caps::eUnreachable; + break; + default:; + } + cap++; + } + } - uint8_t RouterInfo::ExtractAddressCaps (const char * value) const - { - uint8_t caps = 0; - const char * cap = value; - while (*cap) - { - switch (*cap) - { - case CAPS_FLAG_V4: - caps |= AddressCaps::eV4; - break; - case CAPS_FLAG_V6: - caps |= AddressCaps::eV6; - break; - case CAPS_FLAG_SSU_TESTING: - caps |= AddressCaps::eSSUTesting; - break; - case CAPS_FLAG_SSU_INTRODUCER: - caps |= AddressCaps::eSSUIntroducer; - break; - default: ; - } - cap++; - } - return caps; - } + uint8_t RouterInfo::ExtractAddressCaps(const char *value) const { + uint8_t caps = 0; + const char *cap = value; + while (*cap) { + switch (*cap) { + case CAPS_FLAG_V4: + caps |= AddressCaps::eV4; + break; + case CAPS_FLAG_V6: + caps |= AddressCaps::eV6; + break; + case CAPS_FLAG_SSU_TESTING: + caps |= AddressCaps::eSSUTesting; + break; + case CAPS_FLAG_SSU_INTRODUCER: + caps |= AddressCaps::eSSUIntroducer; + break; + default:; + } + cap++; + } + return caps; + } - bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const - { - if (!m_RouterIdentity) return false; - size_t size = m_RouterIdentity->GetFullLen (); - if (size + 8 > len) return false; - return bufbe64toh (buf + size) > m_Timestamp; - } + bool RouterInfo::IsNewer(const uint8_t *buf, size_t len) const { + if (!m_RouterIdentity) return false; + size_t size = m_RouterIdentity->GetFullLen(); + if (size + 8 > len) return false; + return bufbe64toh(buf + size) > m_Timestamp; + } - const uint8_t * RouterInfo::LoadBuffer (const std::string& fullPath) - { - if (!m_Buffer) - { - if (LoadFile (fullPath)) - LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); - } - return m_Buffer->data (); - } + const uint8_t *RouterInfo::LoadBuffer(const std::string &fullPath) { + if (!m_Buffer) { + if (LoadFile(fullPath)) + LogPrint(eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation(GetIdentHash()), + " loaded from file"); + } + return m_Buffer->data(); + } - bool RouterInfo::SaveToFile (const std::string& fullPath) - { - if (!m_Buffer) - { - LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL"); - return false; - } - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) { - LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath); - return false; - } - f.write ((char *)m_Buffer->data (), m_BufferLen); - return true; - } + bool RouterInfo::SaveToFile(const std::string &fullPath) { + if (!m_Buffer) { + LogPrint(eLogError, "RouterInfo: Can't save, m_Buffer == NULL"); + return false; + } + std::ofstream f(fullPath, std::ofstream::binary | std::ofstream::out); + if (!f.is_open()) { + LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath); + return false; + } + f.write((char *) m_Buffer->data(), m_BufferLen); + return true; + } - size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const - { - uint8_t l; - s.read ((char *)&l, 1); - if (l < len) - { - s.read (str, l); - if (!s) l = 0; // failed, return empty string - str[l] = 0; - } - else - { - LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len); - s.seekg (l, std::ios::cur); // skip - str[0] = 0; - } - return l+1; - } + size_t RouterInfo::ReadString(char *str, size_t len, std::istream &s) const { + uint8_t l; + s.read((char *) &l, 1); + if (l < len) { + s.read(str, l); + if (!s) l = 0; // failed, return empty string + str[l] = 0; + } else { + LogPrint(eLogWarning, "RouterInfo: String length ", (int) l, " exceeds buffer size ", len); + s.seekg(l, std::ios::cur); // skip + str[0] = 0; + } + return l + 1; + } - void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) - { - auto addr = std::make_shared
(); - addr->host = boost::asio::ip::address::from_string (host); - addr->port = port; - addr->transportStyle = eTransportSSU; - addr->published = true; - addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = mtu; - if (key) - memcpy (addr->i, key, 32); - else - RAND_bytes (addr->i, 32); - for (const auto& it: *m_Addresses) // don't insert same address twice - if (*it == *addr) return; - m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; - m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; - m_Addresses->push_back(std::move(addr)); - } + void RouterInfo::AddSSUAddress(const char *host, int port, const uint8_t *key, int mtu) { + auto addr = std::make_shared
(); + addr->host = boost::asio::ip::address::from_string(host); + addr->port = port; + addr->transportStyle = eTransportSSU; + addr->published = true; + addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; + addr->date = 0; + addr->ssu.reset(new SSUExt()); + addr->ssu->mtu = mtu; + if (key) + memcpy(addr->i, key, 32); + else + RAND_bytes(addr->i, 32); + for (const auto &it: *m_Addresses) // don't insert same address twice + if (*it == *addr) return; + m_SupportedTransports |= addr->host.is_v6() ? eSSUV6 : eSSUV4; + m_ReachableTransports |= addr->host.is_v6() ? eSSUV6 : eSSUV4; + m_Addresses->push_back(std::move(addr)); + } - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, - const boost::asio::ip::address& host, int port, uint8_t caps) - { - auto addr = std::make_shared
(); - addr->host = host; - addr->port = port; - addr->transportStyle = eTransportNTCP; - addr->caps = caps; - addr->date = 0; - if (port) addr->published = true; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, iv, 16); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eNTCP2V4; - if (addr->published) m_ReachableTransports |= eNTCP2V4; - } - if (addr->IsV6 ()) - { - m_SupportedTransports |= eNTCP2V6; - if (addr->published) m_ReachableTransports |= eNTCP2V6; - } - m_Addresses->push_back(std::move(addr)); - } + void RouterInfo::AddNTCP2Address(const uint8_t *staticKey, const uint8_t *iv, + const boost::asio::ip::address &host, int port, uint8_t caps) { + auto addr = std::make_shared
(); + addr->host = host; + addr->port = port; + addr->transportStyle = eTransportNTCP; + addr->caps = caps; + addr->date = 0; + if (port) addr->published = true; + memcpy(addr->s, staticKey, 32); + memcpy(addr->i, iv, 16); + if (addr->IsV4()) { + m_SupportedTransports |= eNTCP2V4; + if (addr->published) m_ReachableTransports |= eNTCP2V4; + } + if (addr->IsV6()) { + m_SupportedTransports |= eNTCP2V6; + if (addr->published) m_ReachableTransports |= eNTCP2V6; + } + m_Addresses->push_back(std::move(addr)); + } - void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps) - { - auto addr = std::make_shared
(); - addr->transportStyle = eTransportSSU2; - addr->port = 0; - addr->caps = caps; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = 0; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, introKey, 32); - if (addr->IsV4 ()) m_SupportedTransports |= eSSU2V4; - if (addr->IsV6 ()) m_SupportedTransports |= eSSU2V6; - m_Addresses->push_back(std::move(addr)); - } + void RouterInfo::AddSSU2Address(const uint8_t *staticKey, const uint8_t *introKey, uint8_t caps) { + auto addr = std::make_shared
(); + addr->transportStyle = eTransportSSU2; + addr->port = 0; + addr->caps = caps; + addr->date = 0; + addr->ssu.reset(new SSUExt()); + addr->ssu->mtu = 0; + memcpy(addr->s, staticKey, 32); + memcpy(addr->i, introKey, 32); + if (addr->IsV4()) m_SupportedTransports |= eSSU2V4; + if (addr->IsV6()) m_SupportedTransports |= eSSU2V6; + m_Addresses->push_back(std::move(addr)); + } - void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, - const boost::asio::ip::address& host, int port) - { - auto addr = std::make_shared
(); - addr->transportStyle = eTransportSSU2; - addr->host = host; - addr->port = port; - addr->published = true; - addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = 0; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, introKey, 32); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eSSU2V4; - m_ReachableTransports |= eSSU2V4; - } - if (addr->IsV6 ()) - { - m_SupportedTransports |= eSSU2V6; - m_ReachableTransports |= eSSU2V6; - } - m_Addresses->push_back(std::move(addr)); - } + void RouterInfo::AddSSU2Address(const uint8_t *staticKey, const uint8_t *introKey, + const boost::asio::ip::address &host, int port) { + auto addr = std::make_shared
(); + addr->transportStyle = eTransportSSU2; + addr->host = host; + addr->port = port; + addr->published = true; + addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; + addr->date = 0; + addr->ssu.reset(new SSUExt()); + addr->ssu->mtu = 0; + memcpy(addr->s, staticKey, 32); + memcpy(addr->i, introKey, 32); + if (addr->IsV4()) { + m_SupportedTransports |= eSSU2V4; + m_ReachableTransports |= eSSU2V4; + } + if (addr->IsV6()) { + m_SupportedTransports |= eSSU2V6; + m_ReachableTransports |= eSSU2V6; + } + m_Addresses->push_back(std::move(addr)); + } - bool RouterInfo::AddIntroducer (const Introducer& introducer) - { - for (auto& addr : *m_Addresses) - { - if (addr->transportStyle == eTransportSSU && - ((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ()))) - { - for (auto& intro: addr->ssu->introducers) - if (intro.iTag == introducer.iTag) return false; // already presented - addr->ssu->introducers.push_back (introducer); - m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6); - return true; - } - } - return false; - } + bool RouterInfo::AddIntroducer(const Introducer &introducer) { + for (auto &addr: *m_Addresses) { + if (addr->transportStyle == eTransportSSU && + ((addr->IsV4() && introducer.iHost.is_v4()) || (addr->IsV6() && introducer.iHost.is_v6()))) { + for (auto &intro: addr->ssu->introducers) + if (intro.iTag == introducer.iTag) return false; // already presented + addr->ssu->introducers.push_back(introducer); + m_ReachableTransports |= (addr->IsV4() ? eSSUV4 : eSSUV6); + return true; + } + } + return false; + } - bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) - { - for (auto& addr: *m_Addresses) - { - if (addr->transportStyle == eTransportSSU && - ((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ()))) - { - for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) - if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) - { - addr->ssu->introducers.erase (it); - if (addr->ssu->introducers.empty ()) - m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6); - return true; - } - } - } - return false; - } + bool RouterInfo::RemoveIntroducer(const boost::asio::ip::udp::endpoint &e) { + for (auto &addr: *m_Addresses) { + if (addr->transportStyle == eTransportSSU && + ((addr->IsV4() && e.address().is_v4()) || (addr->IsV6() && e.address().is_v6()))) { + for (auto it = addr->ssu->introducers.begin(); it != addr->ssu->introducers.end(); ++it) + if (boost::asio::ip::udp::endpoint(it->iHost, it->iPort) == e) { + addr->ssu->introducers.erase(it); + if (addr->ssu->introducers.empty()) + m_ReachableTransports &= ~(addr->IsV4() ? eSSUV4 : eSSUV6); + return true; + } + } + } + return false; + } - bool RouterInfo::IsSSU (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eSSUV4; - else - return m_SupportedTransports & (eSSUV4 | eSSUV6); - } + bool RouterInfo::IsSSU(bool v4only) const { + if (v4only) + return m_SupportedTransports & eSSUV4; + else + return m_SupportedTransports & (eSSUV4 | eSSUV6); + } - bool RouterInfo::IsNTCP2 (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eNTCP2V4; - else - return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); - } + bool RouterInfo::IsNTCP2(bool v4only) const { + if (v4only) + return m_SupportedTransports & eNTCP2V4; + else + return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); + } - void RouterInfo::EnableV6 () - { - if (!IsV6 ()) - { - uint8_t addressCaps = AddressCaps::eV6; - if (IsV4 ()) addressCaps |= AddressCaps::eV4; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } - } + void RouterInfo::EnableV6() { + if (!IsV6()) { + uint8_t addressCaps = AddressCaps::eV6; + if (IsV4()) addressCaps |= AddressCaps::eV4; + SetUnreachableAddressesTransportCaps(addressCaps); + UpdateSupportedTransports(); + } + } - void RouterInfo::EnableV4 () - { - if (!IsV4 ()) - { - uint8_t addressCaps = AddressCaps::eV4; - if (IsV6 ()) addressCaps |= AddressCaps::eV6; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } - } + void RouterInfo::EnableV4() { + if (!IsV4()) { + uint8_t addressCaps = AddressCaps::eV4; + if (IsV6()) addressCaps |= AddressCaps::eV6; + SetUnreachableAddressesTransportCaps(addressCaps); + UpdateSupportedTransports(); + } + } - void RouterInfo::DisableV6 () - { - if (IsV6 ()) - { - for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) - { - auto addr = *it; - if (addr->IsV6 ()) - { - if (addr->IsV4 ()) - { - addr->caps &= ~AddressCaps::eV6; - ++it; - } - else - it = m_Addresses->erase (it); - } - else - ++it; - } - UpdateSupportedTransports (); - } - } + void RouterInfo::DisableV6() { + if (IsV6()) { + for (auto it = m_Addresses->begin(); it != m_Addresses->end();) { + auto addr = *it; + if (addr->IsV6()) { + if (addr->IsV4()) { + addr->caps &= ~AddressCaps::eV6; + ++it; + } else + it = m_Addresses->erase(it); + } else + ++it; + } + UpdateSupportedTransports(); + } + } - void RouterInfo::DisableV4 () - { - if (IsV4 ()) - { - for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) - { - auto addr = *it; - if (addr->IsV4 ()) - { - if (addr->IsV6 ()) - { - addr->caps &= ~AddressCaps::eV4; - ++it; - } - else - it = m_Addresses->erase (it); - } - else - ++it; - } - UpdateSupportedTransports (); - } - } + void RouterInfo::DisableV4() { + if (IsV4()) { + for (auto it = m_Addresses->begin(); it != m_Addresses->end();) { + auto addr = *it; + if (addr->IsV4()) { + if (addr->IsV6()) { + addr->caps &= ~AddressCaps::eV4; + ++it; + } else + it = m_Addresses->erase(it); + } else + ++it; + } + UpdateSupportedTransports(); + } + } - void RouterInfo::EnableMesh () - { - if (!IsMesh ()) - { - m_SupportedTransports |= eNTCP2V6Mesh; - m_ReachableTransports |= eNTCP2V6Mesh; - } - } + void RouterInfo::EnableMesh() { + if (!IsMesh()) { + m_SupportedTransports |= eNTCP2V6Mesh; + m_ReachableTransports |= eNTCP2V6Mesh; + } + } - void RouterInfo::DisableMesh () - { - if (IsMesh ()) - { - m_SupportedTransports &= ~eNTCP2V6Mesh; - m_ReachableTransports &= ~eNTCP2V6Mesh; - for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) - { - auto addr = *it; - if (i2p::util::net::IsYggdrasilAddress (addr->host)) - it = m_Addresses->erase (it); - else - ++it; - } - } - } + void RouterInfo::DisableMesh() { + if (IsMesh()) { + m_SupportedTransports &= ~eNTCP2V6Mesh; + m_ReachableTransports &= ~eNTCP2V6Mesh; + for (auto it = m_Addresses->begin(); it != m_Addresses->end();) { + auto addr = *it; + if (i2p::util::net::IsYggdrasilAddress(addr->host)) + it = m_Addresses->erase(it); + else + ++it; + } + } + } - std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const - { - return GetAddress ( - [v4only](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ()); - }); - } + std::shared_ptr RouterInfo::GetSSUAddress(bool v4only) const { + return GetAddress( + [v4only](std::shared_ptr address) -> bool { + return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4()); + }); + } - std::shared_ptr RouterInfo::GetSSUV6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsV6(); - }); - } + std::shared_ptr RouterInfo::GetSSUV6Address() const { + return GetAddress( + [](std::shared_ptr address) -> bool { + return (address->transportStyle == eTransportSSU) && address->IsV6(); + }); + } - std::shared_ptr RouterInfo::GetSSU2V4Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU2) && address->IsV4(); - }); - } + std::shared_ptr RouterInfo::GetSSU2V4Address() const { + return GetAddress( + [](std::shared_ptr address) -> bool { + return (address->transportStyle == eTransportSSU2) && address->IsV4(); + }); + } - std::shared_ptr RouterInfo::GetSSU2V6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU2) && address->IsV6(); - }); - } + std::shared_ptr RouterInfo::GetSSU2V6Address() const { + return GetAddress( + [](std::shared_ptr address) -> bool { + return (address->transportStyle == eTransportSSU2) && address->IsV6(); + }); + } - std::shared_ptr RouterInfo::GetSSU2Address (bool v4) const - { - if (v4) - { - if (m_SupportedTransports & eSSU2V4) - return GetSSU2V4Address (); - } - else - { - if (m_SupportedTransports & eSSU2V6) - return GetSSU2V6Address (); - } - return nullptr; - } - - template - std::shared_ptr RouterInfo::GetAddress (Filter filter) const - { - // TODO: make it more generic using comparator + std::shared_ptr RouterInfo::GetSSU2Address(bool v4) const { + if (v4) { + if (m_SupportedTransports & eSSU2V4) + return GetSSU2V4Address(); + } else { + if (m_SupportedTransports & eSSU2V6) + return GetSSU2V6Address(); + } + return nullptr; + } + + template + std::shared_ptr RouterInfo::GetAddress(Filter filter) const { + // TODO: make it more generic using comparator #if (BOOST_VERSION >= 105300) - auto addresses = boost::atomic_load (&m_Addresses); + auto addresses = boost::atomic_load (&m_Addresses); #else - auto addresses = m_Addresses; + auto addresses = m_Addresses; #endif - for (const auto& address : *addresses) - if (filter (address)) return address; + for (const auto &address: *addresses) + if (filter(address)) return address; - return nullptr; - } + return nullptr; + } - std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const - { - if (!key) return nullptr; - return GetAddress ( - [key](std::shared_ptr address)->bool - { - return address->IsNTCP2 () && !memcmp (address->s, key, 32); - }); - } + std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey(const uint8_t *key) const { + if (!key) return nullptr; + return GetAddress( + [key](std::shared_ptr address) -> bool { + return address->IsNTCP2() && !memcmp(address->s, key, 32); + }); + } - std::shared_ptr RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const - { - if (!key) return nullptr; - return GetAddress ( - [key, isV6](std::shared_ptr address)->bool - { - return address->IsSSU2 () && !memcmp (address->s, key, 32) && - ((isV6 && address->IsV6 ()) || (!isV6 && address->IsV4 ())); - }); - } + std::shared_ptr + RouterInfo::GetSSU2AddressWithStaticKey(const uint8_t *key, bool isV6) const { + if (!key) return nullptr; + return GetAddress( + [key, isV6](std::shared_ptr address) -> bool { + return address->IsSSU2() && !memcmp(address->s, key, 32) && + ((isV6 && address->IsV6()) || (!isV6 && address->IsV4())); + }); + } - std::shared_ptr RouterInfo::GetPublishedNTCP2V4Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return address->IsPublishedNTCP2 () && address->host.is_v4 (); - }); - } + std::shared_ptr RouterInfo::GetPublishedNTCP2V4Address() const { + return GetAddress( + [](std::shared_ptr address) -> bool { + return address->IsPublishedNTCP2() && address->host.is_v4(); + }); + } - std::shared_ptr RouterInfo::GetPublishedNTCP2V6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return address->IsPublishedNTCP2 () && address->host.is_v6 () && - !i2p::util::net::IsYggdrasilAddress (address->host); - }); - } + std::shared_ptr RouterInfo::GetPublishedNTCP2V6Address() const { + return GetAddress( + [](std::shared_ptr address) -> bool { + return address->IsPublishedNTCP2() && address->host.is_v6() && + !i2p::util::net::IsYggdrasilAddress(address->host); + }); + } - std::shared_ptr RouterInfo::GetYggdrasilAddress () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return address->IsPublishedNTCP2 () && i2p::util::net::IsYggdrasilAddress (address->host); - }); - } + std::shared_ptr RouterInfo::GetYggdrasilAddress() const { + return GetAddress( + [](std::shared_ptr address) -> bool { + return address->IsPublishedNTCP2() && i2p::util::net::IsYggdrasilAddress(address->host); + }); + } - std::shared_ptr RouterInfo::GetProfile () const - { - if (!m_Profile) - m_Profile = GetRouterProfile (GetIdentHash ()); - return m_Profile; - } + std::shared_ptr RouterInfo::GetProfile() const { + if (!m_Profile) + m_Profile = GetRouterProfile(GetIdentHash()); + return m_Profile; + } - void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const - { - auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); - if (encryptor) - encryptor->Encrypt (data, encrypted); - } + void RouterInfo::Encrypt(const uint8_t *data, uint8_t *encrypted) const { + auto encryptor = m_RouterIdentity->CreateEncryptor(nullptr); + if (encryptor) + encryptor->Encrypt(data, encrypted); + } - bool RouterInfo::IsEligibleFloodfill () const - { - // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA - return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION && - GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; - } + bool RouterInfo::IsEligibleFloodfill() const { + // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA + return IsReachableBy(eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION && + GetIdentity()->GetSigningKeyType() != SIGNING_KEY_TYPE_DSA_SHA1; + } - bool RouterInfo::IsPeerTesting (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU (); - }); - } + bool RouterInfo::IsPeerTesting(bool v4) const { + if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; + return (bool) GetAddress( + [v4](std::shared_ptr address) -> bool { + return (address->transportStyle == eTransportSSU) && address->IsPeerTesting() && + ((v4 && address->IsV4()) || (!v4 && address->IsV6())) && address->IsReachableSSU(); + }); + } - bool RouterInfo::IsSSU2PeerTesting (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->IsSSU2 ()) && address->IsPeerTesting () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU (); - }); - } - - bool RouterInfo::IsIntroducer (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsIntroducer () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified (); - }); - } + bool RouterInfo::IsSSU2PeerTesting(bool v4) const { + if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; + return (bool) GetAddress( + [v4](std::shared_ptr address) -> bool { + return (address->IsSSU2()) && address->IsPeerTesting() && + ((v4 && address->IsV4()) || (!v4 && address->IsV6())) && address->IsReachableSSU(); + }); + } - bool RouterInfo::IsSSU2Introducer (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->IsSSU2 ()) && address->IsIntroducer () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified (); - }); - } - - void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) - { - for (auto& addr: *m_Addresses) - { - // TODO: implement SSU - if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2)) - { - addr->caps &= ~(eV4 | eV6); - addr->caps |= transports; - } - } - } + bool RouterInfo::IsIntroducer(bool v4) const { + if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; + return (bool) GetAddress( + [v4](std::shared_ptr address) -> bool { + return (address->transportStyle == eTransportSSU) && address->IsIntroducer() && + ((v4 && address->IsV4()) || (!v4 && address->IsV6())) && !address->host.is_unspecified(); + }); + } - void RouterInfo::UpdateSupportedTransports () - { - m_SupportedTransports = 0; - m_ReachableTransports = 0; - for (const auto& addr: *m_Addresses) - { - uint8_t transports = 0; - switch (addr->transportStyle) - { - case eTransportNTCP: - if (addr->IsV4 ()) transports |= eNTCP2V4; - if (addr->IsV6 ()) - transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6); - if (addr->IsPublishedNTCP2 ()) - m_ReachableTransports |= transports; - break; - case eTransportSSU: - if (addr->IsV4 ()) transports |= eSSUV4; - if (addr->IsV6 ()) transports |= eSSUV6; - if (addr->IsReachableSSU ()) - m_ReachableTransports |= transports; - break; - case eTransportSSU2: - if (addr->IsV4 ()) transports |= eSSU2V4; - if (addr->IsV6 ()) transports |= eSSU2V6; - if (addr->IsReachableSSU ()) - m_ReachableTransports |= transports; - break; - default: ; - } - m_SupportedTransports |= transports; - } - } + bool RouterInfo::IsSSU2Introducer(bool v4) const { + if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; + return (bool) GetAddress( + [v4](std::shared_ptr address) -> bool { + return (address->IsSSU2()) && address->IsIntroducer() && + ((v4 && address->IsV4()) || (!v4 && address->IsV6())) && !address->host.is_unspecified(); + }); + } - void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len) - { - if (!m_Buffer) - m_Buffer = NewBuffer (); - if (len > m_Buffer->size ()) len = m_Buffer->size (); - memcpy (m_Buffer->data (), buf, len); - m_BufferLen = len; - } + void RouterInfo::SetUnreachableAddressesTransportCaps(uint8_t transports) { + for (auto &addr: *m_Addresses) { + // TODO: implement SSU + if (!addr->published && + (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2)) { + addr->caps &= ~(eV4 | eV6); + addr->caps |= transports; + } + } + } - std::shared_ptr RouterInfo::NewBuffer () const - { - return netdb.NewRouterInfoBuffer (); - } + void RouterInfo::UpdateSupportedTransports() { + m_SupportedTransports = 0; + m_ReachableTransports = 0; + for (const auto &addr: *m_Addresses) { + uint8_t transports = 0; + switch (addr->transportStyle) { + case eTransportNTCP: + if (addr->IsV4()) transports |= eNTCP2V4; + if (addr->IsV6()) + transports |= (i2p::util::net::IsYggdrasilAddress(addr->host) ? eNTCP2V6Mesh : eNTCP2V6); + if (addr->IsPublishedNTCP2()) + m_ReachableTransports |= transports; + break; + case eTransportSSU: + if (addr->IsV4()) transports |= eSSUV4; + if (addr->IsV6()) transports |= eSSUV6; + if (addr->IsReachableSSU()) + m_ReachableTransports |= transports; + break; + case eTransportSSU2: + if (addr->IsV4()) transports |= eSSU2V4; + if (addr->IsV6()) transports |= eSSU2V6; + if (addr->IsReachableSSU()) + m_ReachableTransports |= transports; + break; + default:; + } + m_SupportedTransports |= transports; + } + } - void RouterInfo::RefreshTimestamp () - { - m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); - } + void RouterInfo::UpdateBuffer(const uint8_t *buf, size_t len) { + if (!m_Buffer) + m_Buffer = NewBuffer(); + if (len > m_Buffer->size()) len = m_Buffer->size(); + memcpy(m_Buffer->data(), buf, len); + m_BufferLen = len; + } - void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) - { - RefreshTimestamp (); - std::stringstream s; - uint8_t ident[1024]; - auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024); - auto signatureLen = privateKeys.GetPublic ()->GetSignatureLen (); - s.write ((char *)ident, identLen); - WriteToStream (s); - size_t len = s.str ().size (); - if (len + signatureLen < MAX_RI_BUFFER_SIZE) - { - UpdateBuffer ((const uint8_t *)s.str ().c_str (), len); - // signature - privateKeys.Sign (GetBuffer (), len, GetBufferPointer (len)); - SetBufferLen (len + signatureLen); - } - else - LogPrint (eLogError, "RouterInfo: Our RouterInfo is too long ", len + signatureLen); - } + std::shared_ptr RouterInfo::NewBuffer() const { + return netdb.NewRouterInfoBuffer(); + } - void LocalRouterInfo::UpdateCaps (uint8_t caps) - { - SetCaps (caps); - UpdateCapsProperty (); - } + void RouterInfo::RefreshTimestamp() { + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch(); + } - void LocalRouterInfo::UpdateCapsProperty () - { - std::string caps; - uint8_t c = GetCaps (); - if (c & eFloodfill) - { - if (c & eExtraBandwidth) caps += (c & eHighBandwidth) ? - CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' - CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' - else - caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' - caps += CAPS_FLAG_FLOODFILL; // floodfill - } - else - { - if (c & eExtraBandwidth) - caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ - else - caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth - } - if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden - if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable - if (c & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable + void LocalRouterInfo::CreateBuffer(const PrivateKeys &privateKeys) { + RefreshTimestamp(); + std::stringstream s; + uint8_t ident[1024]; + auto identLen = privateKeys.GetPublic()->ToBuffer(ident, 1024); + auto signatureLen = privateKeys.GetPublic()->GetSignatureLen(); + s.write((char *) ident, identLen); + WriteToStream(s); + size_t len = s.str().size(); + if (len + signatureLen < MAX_RI_BUFFER_SIZE) { + UpdateBuffer((const uint8_t *) s.str().c_str(), len); + // signature + privateKeys.Sign(GetBuffer(), len, GetBufferPointer(len)); + SetBufferLen(len + signatureLen); + } else + LogPrint(eLogError, "RouterInfo: Our RouterInfo is too long ", len + signatureLen); + } - SetProperty ("caps", caps); - } + void LocalRouterInfo::UpdateCaps(uint8_t caps) { + SetCaps(caps); + UpdateCapsProperty(); + } - void LocalRouterInfo::WriteToStream (std::ostream& s) const - { - uint64_t ts = htobe64 (GetTimestamp ()); - s.write ((const char *)&ts, sizeof (ts)); + void LocalRouterInfo::UpdateCapsProperty() { + std::string caps; + uint8_t c = GetCaps(); + if (c & eFloodfill) { + if (c & eExtraBandwidth) + caps += (c & eHighBandwidth) ? + CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' + CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' + else + caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' + caps += CAPS_FLAG_FLOODFILL; // floodfill + } else { + if (c & eExtraBandwidth) + caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ + : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ + else + caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */ + : CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth + } + if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden + if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable + if (c & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable - // addresses - const Addresses& addresses = GetAddresses (); - uint8_t numAddresses = addresses.size (); - s.write ((char *)&numAddresses, sizeof (numAddresses)); - for (const auto& addr_ptr : addresses) - { - const Address& address = *addr_ptr; - // calculate cost - uint8_t cost = 0x7f; - if (address.transportStyle == eTransportNTCP) - cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; - else if (address.transportStyle == eTransportSSU) - cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS; - else if (address.transportStyle == eTransportSSU2) - cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED; - s.write ((const char *)&cost, sizeof (cost)); - s.write ((const char *)&address.date, sizeof (address.date)); - std::stringstream properties; - bool isPublished = false; - if (address.transportStyle == eTransportNTCP) - { - if (address.IsNTCP2 ()) - { - WriteString ("NTCP2", s); - if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port) - isPublished = true; - else - { - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 ()) caps += CAPS_FLAG_V6; - if (caps.empty ()) caps += CAPS_FLAG_V4; - WriteString (caps, properties); - properties << ';'; - } - } - else - continue; // don't write NTCP address - } - else if (address.transportStyle == eTransportSSU) - { - WriteString ("SSU", s); - // caps - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.host.is_v4 ()) - { - if (address.published) - { - isPublished = true; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - caps += CAPS_FLAG_V4; - } - else if (address.host.is_v6 ()) - { - if (address.published) - { - isPublished = true; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - caps += CAPS_FLAG_V6; - } - else - { - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 ()) caps += CAPS_FLAG_V6; - if (caps.empty ()) caps += CAPS_FLAG_V4; - } - WriteString (caps, properties); - properties << ';'; - } - else if (address.transportStyle == eTransportSSU2) - { - WriteString ("SSU2", s); - // caps - std::string caps; - if (address.published) - { - isPublished = true; - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - { - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 ()) caps += CAPS_FLAG_V6; - if (caps.empty ()) caps += CAPS_FLAG_V4; - } - if (!caps.empty ()) - { - WriteString ("caps", properties); - properties << '='; - WriteString (caps, properties); - properties << ';'; - } - } - else - WriteString ("", s); + SetProperty("caps", caps); + } - if (isPublished && !address.host.is_unspecified ()) - { - WriteString ("host", properties); - properties << '='; - WriteString (address.host.to_string (), properties); - properties << ';'; - } - if ((address.IsNTCP2 () && isPublished) || address.IsSSU2 ()) - { - // publish i for NTCP2 or SSU2 - WriteString ("i", properties); properties << '='; - size_t len = address.IsSSU2 () ? 32 : 16; - WriteString (address.i.ToBase64 (len), properties); properties << ';'; - } - if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) - { - // write introducers if any - if (address.ssu && !address.ssu->introducers.empty()) - { - int i = 0; - for (const auto& introducer: address.ssu->introducers) - { - if (introducer.iExp) // expiration is specified - { - WriteString ("iexp" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iExp), properties); - properties << ';'; - } - i++; - } - if (address.transportStyle == eTransportSSU) - { - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("ihost" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (introducer.iHost.to_string (), properties); - properties << ';'; - i++; - } - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - if (address.IsSSU2 ()) - WriteString ("ih" + boost::lexical_cast(i), properties); - else - WriteString ("ikey" + boost::lexical_cast(i), properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - i++; - } - if (address.transportStyle == eTransportSSU) - { - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("iport" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iPort), properties); - properties << ';'; - i++; - } - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("itag" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iTag), properties); - properties << ';'; - i++; - } - } - } - if (address.transportStyle == eTransportSSU) - { - // write intro key - WriteString ("key", properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (address.i, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - } - if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) - { - // write mtu - if (address.ssu && address.ssu->mtu) - { - WriteString ("mtu", properties); - properties << '='; - WriteString (boost::lexical_cast(address.ssu->mtu), properties); - properties << ';'; - } - } - if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port) - { - WriteString ("port", properties); - properties << '='; - WriteString (boost::lexical_cast(address.port), properties); - properties << ';'; - } - if (address.IsNTCP2 () || address.IsSSU2 ()) - { - // publish s and v for NTCP2 or SSU2 - WriteString ("s", properties); properties << '='; - WriteString (address.s.ToBase64 (), properties); properties << ';'; - WriteString ("v", properties); properties << '='; - WriteString ("2", properties); properties << ';'; - } + void LocalRouterInfo::WriteToStream(std::ostream &s) const { + uint64_t ts = htobe64(GetTimestamp()); + s.write((const char *) &ts, sizeof(ts)); - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } + // addresses + const Addresses &addresses = GetAddresses(); + uint8_t numAddresses = addresses.size(); + s.write((char *) &numAddresses, sizeof(numAddresses)); + for (const auto &addr_ptr: addresses) { + const Address &address = *addr_ptr; + // calculate cost + uint8_t cost = 0x7f; + if (address.transportStyle == eTransportNTCP) + cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; + else if (address.transportStyle == eTransportSSU) + cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS; + else if (address.transportStyle == eTransportSSU2) + cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED; + s.write((const char *) &cost, sizeof(cost)); + s.write((const char *) &address.date, sizeof(address.date)); + std::stringstream properties; + bool isPublished = false; + if (address.transportStyle == eTransportNTCP) { + if (address.IsNTCP2()) { + WriteString("NTCP2", s); + if (address.IsPublishedNTCP2() && !address.host.is_unspecified() && address.port) + isPublished = true; + else { + WriteString("caps", properties); + properties << '='; + std::string caps; + if (address.IsV4()) caps += CAPS_FLAG_V4; + if (address.IsV6()) caps += CAPS_FLAG_V6; + if (caps.empty()) caps += CAPS_FLAG_V4; + WriteString(caps, properties); + properties << ';'; + } + } else + continue; // don't write NTCP address + } else if (address.transportStyle == eTransportSSU) { + WriteString("SSU", s); + // caps + WriteString("caps", properties); + properties << '='; + std::string caps; + if (address.IsPeerTesting()) caps += CAPS_FLAG_SSU_TESTING; + if (address.host.is_v4()) { + if (address.published) { + isPublished = true; + if (address.IsIntroducer()) caps += CAPS_FLAG_SSU_INTRODUCER; + } else + caps += CAPS_FLAG_V4; + } else if (address.host.is_v6()) { + if (address.published) { + isPublished = true; + if (address.IsIntroducer()) caps += CAPS_FLAG_SSU_INTRODUCER; + } else + caps += CAPS_FLAG_V6; + } else { + if (address.IsV4()) caps += CAPS_FLAG_V4; + if (address.IsV6()) caps += CAPS_FLAG_V6; + if (caps.empty()) caps += CAPS_FLAG_V4; + } + WriteString(caps, properties); + properties << ';'; + } else if (address.transportStyle == eTransportSSU2) { + WriteString("SSU2", s); + // caps + std::string caps; + if (address.published) { + isPublished = true; + if (address.IsPeerTesting()) caps += CAPS_FLAG_SSU_TESTING; + if (address.IsIntroducer()) caps += CAPS_FLAG_SSU_INTRODUCER; + } else { + if (address.IsV4()) caps += CAPS_FLAG_V4; + if (address.IsV6()) caps += CAPS_FLAG_V6; + if (caps.empty()) caps += CAPS_FLAG_V4; + } + if (!caps.empty()) { + WriteString("caps", properties); + properties << '='; + WriteString(caps, properties); + properties << ';'; + } + } else + WriteString("", s); - // peers - uint8_t numPeers = 0; - s.write ((char *)&numPeers, sizeof (numPeers)); + if (isPublished && !address.host.is_unspecified()) { + WriteString("host", properties); + properties << '='; + WriteString(address.host.to_string(), properties); + properties << ';'; + } + if ((address.IsNTCP2() && isPublished) || address.IsSSU2()) { + // publish i for NTCP2 or SSU2 + WriteString("i", properties); + properties << '='; + size_t len = address.IsSSU2() ? 32 : 16; + WriteString(address.i.ToBase64(len), properties); + properties << ';'; + } + if (address.transportStyle == eTransportSSU || address.IsSSU2()) { + // write introducers if any + if (address.ssu && !address.ssu->introducers.empty()) { + int i = 0; + for (const auto &introducer: address.ssu->introducers) { + if (introducer.iExp) // expiration is specified + { + WriteString("iexp" + boost::lexical_cast(i), properties); + properties << '='; + WriteString(boost::lexical_cast(introducer.iExp), properties); + properties << ';'; + } + i++; + } + if (address.transportStyle == eTransportSSU) { + i = 0; + for (const auto &introducer: address.ssu->introducers) { + WriteString("ihost" + boost::lexical_cast(i), properties); + properties << '='; + WriteString(introducer.iHost.to_string(), properties); + properties << ';'; + i++; + } + } + i = 0; + for (const auto &introducer: address.ssu->introducers) { + if (address.IsSSU2()) + WriteString("ih" + boost::lexical_cast(i), properties); + else + WriteString("ikey" + boost::lexical_cast(i), properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64(introducer.iKey, 32, value, 64); + value[l] = 0; + WriteString(value, properties); + properties << ';'; + i++; + } + if (address.transportStyle == eTransportSSU) { + i = 0; + for (const auto &introducer: address.ssu->introducers) { + WriteString("iport" + boost::lexical_cast(i), properties); + properties << '='; + WriteString(boost::lexical_cast(introducer.iPort), properties); + properties << ';'; + i++; + } + } + i = 0; + for (const auto &introducer: address.ssu->introducers) { + WriteString("itag" + boost::lexical_cast(i), properties); + properties << '='; + WriteString(boost::lexical_cast(introducer.iTag), properties); + properties << ';'; + i++; + } + } + } + if (address.transportStyle == eTransportSSU) { + // write intro key + WriteString("key", properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64(address.i, 32, value, 64); + value[l] = 0; + WriteString(value, properties); + properties << ';'; + } + if (address.transportStyle == eTransportSSU || address.IsSSU2()) { + // write mtu + if (address.ssu && address.ssu->mtu) { + WriteString("mtu", properties); + properties << '='; + WriteString(boost::lexical_cast(address.ssu->mtu), properties); + properties << ';'; + } + } + if ((isPublished || (address.ssu && !address.IsSSU2())) && address.port) { + WriteString("port", properties); + properties << '='; + WriteString(boost::lexical_cast(address.port), properties); + properties << ';'; + } + if (address.IsNTCP2() || address.IsSSU2()) { + // publish s and v for NTCP2 or SSU2 + WriteString("s", properties); + properties << '='; + WriteString(address.s.ToBase64(), properties); + properties << ';'; + WriteString("v", properties); + properties << '='; + WriteString("2", properties); + properties << ';'; + } - // properties - std::stringstream properties; - for (const auto& p : m_Properties) - { - WriteString (p.first, properties); - properties << '='; - WriteString (p.second, properties); - properties << ';'; - } - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } + uint16_t size = htobe16(properties.str().size()); + s.write((char *) &size, sizeof(size)); + s.write(properties.str().c_str(), properties.str().size()); + } - void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value) - { - m_Properties[key] = value; - } + // peers + uint8_t numPeers = 0; + s.write((char *) &numPeers, sizeof(numPeers)); - void LocalRouterInfo::DeleteProperty (const std::string& key) - { - m_Properties.erase (key); - } + // properties + std::stringstream properties; + for (const auto &p: m_Properties) { + WriteString(p.first, properties); + properties << '='; + WriteString(p.second, properties); + properties << ';'; + } + uint16_t size = htobe16(properties.str().size()); + s.write((char *) &size, sizeof(size)); + s.write(properties.str().c_str(), properties.str().size()); + } - std::string LocalRouterInfo::GetProperty (const std::string& key) const - { - auto it = m_Properties.find (key); - if (it != m_Properties.end ()) - return it->second; - return ""; - } + void LocalRouterInfo::SetProperty(const std::string &key, const std::string &value) { + m_Properties[key] = value; + } - void LocalRouterInfo::WriteString (const std::string& str, std::ostream& s) const - { - uint8_t len = str.size (); - s.write ((char *)&len, 1); - s.write (str.c_str (), len); - } + void LocalRouterInfo::DeleteProperty(const std::string &key) { + m_Properties.erase(key); + } - std::shared_ptr LocalRouterInfo::NewBuffer () const - { - return std::make_shared (); - } + std::string LocalRouterInfo::GetProperty(const std::string &key) const { + auto it = m_Properties.find(key); + if (it != m_Properties.end()) + return it->second; + return ""; + } - bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4) - { - for (auto& addr : GetAddresses ()) - { - if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) - { - for (auto& intro: addr->ssu->introducers) - if (intro.iTag == introducer.iTag) return false; // already presented - addr->ssu->introducers.push_back (introducer); - SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6))); - return true; - } - } - return false; - } + void LocalRouterInfo::WriteString(const std::string &str, std::ostream &s) const { + uint8_t len = str.size(); + s.write((char *) &len, 1); + s.write(str.c_str(), len); + } - bool LocalRouterInfo::RemoveSSU2Introducer (const IdentHash& h, bool v4) - { - for (auto& addr: GetAddresses ()) - { - if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) - { - for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) - if (h == it->iKey) - { - addr->ssu->introducers.erase (it); - if (addr->ssu->introducers.empty ()) - SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6)); - return true; - } - } - } - return false; - } -} + std::shared_ptr LocalRouterInfo::NewBuffer() const { + return std::make_shared(); + } + + bool LocalRouterInfo::AddSSU2Introducer(const Introducer &introducer, bool v4) { + for (auto &addr: GetAddresses()) { + if (addr->IsSSU2() && ((v4 && addr->IsV4()) || (!v4 && addr->IsV6()))) { + for (auto &intro: addr->ssu->introducers) + if (intro.iTag == introducer.iTag) return false; // already presented + addr->ssu->introducers.push_back(introducer); + SetReachableTransports(GetReachableTransports() | ((addr->IsV4() ? eSSU2V4 : eSSU2V6))); + return true; + } + } + return false; + } + + bool LocalRouterInfo::RemoveSSU2Introducer(const IdentHash &h, bool v4) { + for (auto &addr: GetAddresses()) { + if (addr->IsSSU2() && ((v4 && addr->IsV4()) || (!v4 && addr->IsV6()))) { + for (auto it = addr->ssu->introducers.begin(); it != addr->ssu->introducers.end(); ++it) + if (h == it->iKey) { + addr->ssu->introducers.erase(it); + if (addr->ssu->introducers.empty()) + SetReachableTransports(GetReachableTransports() & ~(addr->IsV4() ? eSSU2V4 : eSSU2V6)); + return true; + } + } + } + return false; + } + } } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 6bc66b35..052335f8 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -21,319 +21,410 @@ #include "Profiling.h" #include "Family.h" -namespace i2p -{ -namespace data -{ - const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; - const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; - const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; - const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version"; - const char ROUTER_INFO_PROPERTY_FAMILY[] = "family"; - const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig"; +namespace i2p { + namespace data { + const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; + const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; + const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; + const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version"; + const char ROUTER_INFO_PROPERTY_FAMILY[] = "family"; + const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig"; - const char CAPS_FLAG_FLOODFILL = 'f'; - const char CAPS_FLAG_HIDDEN = 'H'; - const char CAPS_FLAG_REACHABLE = 'R'; - const char CAPS_FLAG_UNREACHABLE = 'U'; - /* bandwidth flags */ - const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 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_BANDWIDTH2 = 'N'; /* 64-128 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_BANDWIDTH2 = 'X'; /* > 2000 KBps */ + const char CAPS_FLAG_FLOODFILL = 'f'; + const char CAPS_FLAG_HIDDEN = 'H'; + const char CAPS_FLAG_REACHABLE = 'R'; + const char CAPS_FLAG_UNREACHABLE = 'U'; + /* bandwidth flags */ + const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 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_BANDWIDTH2 = 'N'; /* 64-128 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_BANDWIDTH2 = 'X'; /* > 2000 KBps */ - const char CAPS_FLAG_V4 = '4'; - const char CAPS_FLAG_V6 = '6'; - const char CAPS_FLAG_SSU_TESTING = 'B'; - const char CAPS_FLAG_SSU_INTRODUCER = 'C'; + const char CAPS_FLAG_V4 = '4'; + const char CAPS_FLAG_V6 = '6'; + const char CAPS_FLAG_SSU_TESTING = 'B'; + const char CAPS_FLAG_SSU_INTRODUCER = 'C'; - const uint8_t COST_NTCP2_PUBLISHED = 3; - const uint8_t COST_NTCP2_NON_PUBLISHED = 14; - const uint8_t COST_SSU2_DIRECT = 8; - const uint8_t COST_SSU_DIRECT = 9; - const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11; - const uint8_t COST_SSU2_NON_PUBLISHED = 15; + const uint8_t COST_NTCP2_PUBLISHED = 3; + const uint8_t COST_NTCP2_NON_PUBLISHED = 14; + const uint8_t COST_SSU2_DIRECT = 8; + const uint8_t COST_SSU_DIRECT = 9; + const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11; + 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 - class RouterInfo: public RoutingDestination - { - public: + const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later + class RouterInfo : public RoutingDestination { + public: - enum SupportedTransports - { - eNTCP2V4 = 0x01, - eNTCP2V6 = 0x02, - eSSUV4 = 0x04, - eSSUV6 = 0x08, - eNTCP2V6Mesh = 0x10, - eSSU2V4 = 0x20, - eSSU2V6 = 0x40, - eAllTransports = 0xFF - }; - typedef uint8_t CompatibleTransports; + enum SupportedTransports { + eNTCP2V4 = 0x01, + eNTCP2V6 = 0x02, + eSSUV4 = 0x04, + eSSUV6 = 0x08, + eNTCP2V6Mesh = 0x10, + eSSU2V4 = 0x20, + eSSU2V6 = 0x40, + eAllTransports = 0xFF + }; + typedef uint8_t CompatibleTransports; - enum Caps - { - eFloodfill = 0x01, - eHighBandwidth = 0x02, - eExtraBandwidth = 0x04, - eReachable = 0x08, - eHidden = 0x10, - eUnreachable = 0x20 - }; + enum Caps { + eFloodfill = 0x01, + eHighBandwidth = 0x02, + eExtraBandwidth = 0x04, + eReachable = 0x08, + eHidden = 0x10, + eUnreachable = 0x20 + }; - enum AddressCaps - { - eV4 = 0x01, - eV6 = 0x02, - eSSUTesting = 0x04, - eSSUIntroducer = 0x08 - }; + enum AddressCaps { + eV4 = 0x01, + eV6 = 0x02, + eSSUTesting = 0x04, + eSSUIntroducer = 0x08 + }; - enum TransportStyle - { - eTransportUnknown = 0, - eTransportNTCP, - eTransportSSU, - eTransportSSU2 - }; + enum TransportStyle { + eTransportUnknown = 0, + eTransportNTCP, + eTransportSSU, + eTransportSSU2 + }; - typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey - struct Introducer - { - Introducer (): iPort (0), iExp (0) {}; - boost::asio::ip::address iHost; - int iPort; - IntroKey iKey; // or ih for SSU2 - uint32_t iTag; - uint32_t iExp; - }; + typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey + struct Introducer { + Introducer() : iPort(0), iExp(0) {}; + boost::asio::ip::address iHost; + int iPort; + IntroKey iKey; // or ih for SSU2 + uint32_t iTag; + uint32_t iExp; + }; - struct SSUExt - { - int mtu; - std::vector introducers; - }; + struct SSUExt { + int mtu; + std::vector introducers; + }; - struct Address - { - TransportStyle transportStyle; - boost::asio::ip::address host; - Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU - int port; - uint64_t date; - uint8_t caps; - bool published = false; - std::unique_ptr ssu; // not null for SSU + struct Address { + TransportStyle transportStyle; + boost::asio::ip::address host; + Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU + int port; + uint64_t date; + uint8_t caps; + bool published = false; + std::unique_ptr ssu; // not null for SSU - bool IsCompatible (const boost::asio::ip::address& other) const - { - return (IsV4 () && other.is_v4 ()) || - (IsV6 () && other.is_v6 ()); - } + bool IsCompatible(const boost::asio::ip::address &other) const { + return (IsV4() && other.is_v4()) || + (IsV6() && other.is_v6()); + } - bool operator==(const Address& other) const - { - return transportStyle == other.transportStyle && - host == other.host && port == other.port; - } + bool operator==(const Address &other) const { + return transportStyle == other.transportStyle && + host == other.host && port == other.port; + } - bool operator!=(const Address& other) const - { - return !(*this == other); - } + bool operator!=(const Address &other) const { + return !(*this == other); + } - 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 IsNTCP2() const { return transportStyle == eTransportNTCP; }; - bool IsIntroducer () const { return caps & eSSUIntroducer; }; - bool IsPeerTesting () const { return caps & eSSUTesting; }; + bool IsSSU2() const { return transportStyle == eTransportSSU2; }; - bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; - bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); }; - }; + bool IsPublishedNTCP2() const { return IsNTCP2() && published; }; - class Buffer: public std::array - { - public: + bool IsReachableSSU() const { return (bool) ssu && (published || UsesIntroducer()); }; - Buffer () = default; - Buffer (const uint8_t * buf, size_t len); - }; + bool UsesIntroducer() const { return (bool) ssu && !ssu->introducers.empty(); }; - typedef std::vector > Addresses; + bool IsIntroducer() const { return caps & eSSUIntroducer; }; - RouterInfo (const std::string& fullPath); - RouterInfo (const RouterInfo& ) = default; - RouterInfo& operator=(const RouterInfo& ) = default; - RouterInfo (std::shared_ptr&& buf, size_t len); - RouterInfo (const uint8_t * buf, size_t len); - virtual ~RouterInfo (); + bool IsPeerTesting() const { return caps & eSSUTesting; }; - std::shared_ptr GetRouterIdentity () const { return m_RouterIdentity; }; - void SetRouterIdentity (std::shared_ptr identity); - std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; - uint64_t GetTimestamp () const { return m_Timestamp; }; - int GetVersion () const { return m_Version; }; - 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 GetNTCP2AddressWithStaticKey (const uint8_t * key) const; - std::shared_ptr GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const; - std::shared_ptr GetPublishedNTCP2V4Address () const; - std::shared_ptr GetPublishedNTCP2V6Address () const; - std::shared_ptr GetSSUAddress (bool v4only = true) const; - std::shared_ptr GetSSUV6Address () const; - std::shared_ptr GetYggdrasilAddress () const; - std::shared_ptr GetSSU2V4Address () const; - std::shared_ptr GetSSU2V6Address () const; - std::shared_ptr GetSSU2Address (bool v4) const; + bool IsV4() const { return (caps & AddressCaps::eV4) || (host.is_v4() && !host.is_unspecified()); }; - 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; + bool IsV6() const { return (caps & AddressCaps::eV6) || (host.is_v6() && !host.is_unspecified()); }; + }; - uint8_t GetCaps () const { return m_Caps; }; - void SetCaps (uint8_t caps) { m_Caps = caps; }; + class Buffer : public std::array { + public: - void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; - bool IsUnreachable () const { return m_IsUnreachable; }; + Buffer() = default; - 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; }; + Buffer(const uint8_t *buf, size_t len); + }; - bool IsUpdated () const { return m_IsUpdated; }; - void SetUpdated (bool updated) { m_IsUpdated = updated; }; - bool SaveToFile (const std::string& fullPath); + typedef std::vector > Addresses; - std::shared_ptr GetProfile () const; - void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); }; + RouterInfo(const std::string &fullPath); - void Update (const uint8_t * buf, size_t len); - void DeleteBuffer () { m_Buffer = nullptr; }; - bool IsNewer (const uint8_t * buf, size_t len) const; + RouterInfo(const RouterInfo &) = default; - /** return true if we are in a router family and the signature is valid */ - bool IsFamily (FamilyID famid) const; + RouterInfo &operator=(const RouterInfo &) = default; - // implements RoutingDestination - std::shared_ptr GetIdentity () const { return m_RouterIdentity; }; - void Encrypt (const uint8_t * data, uint8_t * encrypted) const; + RouterInfo(std::shared_ptr &&buf, size_t len); - bool IsDestination () const { return false; }; + RouterInfo(const uint8_t *buf, size_t len); - protected: + virtual ~RouterInfo(); - 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; }; + std::shared_ptr GetRouterIdentity() const { return m_RouterIdentity; }; - private: + void SetRouterIdentity(std::shared_ptr identity); - 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 - std::shared_ptr GetAddress (Filter filter) const; - virtual std::shared_ptr NewBuffer () const; + std::string GetIdentHashBase64() const { return GetIdentHash().ToBase64(); }; - private: + uint64_t GetTimestamp() const { return m_Timestamp; }; - FamilyID m_FamilyID; - std::shared_ptr m_RouterIdentity; - std::shared_ptr m_Buffer; - size_t m_BufferLen; - uint64_t m_Timestamp; - boost::shared_ptr 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 m_Profile; - }; + int GetVersion() const { return m_Version; }; - class LocalRouterInfo: public RouterInfo - { - public: + virtual void SetProperty(const std::string &key, const std::string &value) {}; - LocalRouterInfo () = default; - void CreateBuffer (const PrivateKeys& privateKeys); - void UpdateCaps (uint8_t caps); + virtual void ClearProperties() {}; - 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 (); }; + Addresses & + GetAddresses() { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr + std::shared_ptr GetNTCP2AddressWithStaticKey(const uint8_t *key) const; - bool AddSSU2Introducer (const Introducer& introducer, bool v4); - bool RemoveSSU2Introducer (const IdentHash& h, bool v4); - - private: + std::shared_ptr GetSSU2AddressWithStaticKey(const uint8_t *key, bool isV6) const; - void WriteToStream (std::ostream& s) const; - void UpdateCapsProperty (); - void WriteString (const std::string& str, std::ostream& s) const; - std::shared_ptr NewBuffer () const override; + std::shared_ptr GetPublishedNTCP2V4Address() const; - private: + std::shared_ptr GetPublishedNTCP2V6Address() const; - std::map m_Properties; - }; -} + std::shared_ptr GetSSUAddress(bool v4only = true) const; + + std::shared_ptr GetSSUV6Address() const; + + std::shared_ptr GetYggdrasilAddress() const; + + std::shared_ptr GetSSU2V4Address() const; + + std::shared_ptr GetSSU2V6Address() const; + + std::shared_ptr 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 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 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 + std::shared_ptr GetAddress(Filter filter) const; + + virtual std::shared_ptr NewBuffer() const; + + private: + + FamilyID m_FamilyID; + std::shared_ptr m_RouterIdentity; + std::shared_ptr m_Buffer; + size_t m_BufferLen; + uint64_t m_Timestamp; + boost::shared_ptr 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 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 NewBuffer() const override; + + private: + + std::map m_Properties; + }; + } } #endif diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index eec55857..f5233f6b 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -16,981 +16,858 @@ #include "SSU.h" #if defined(__linux__) && !defined(_NETINET_IN_H) - #include +#include #endif #ifdef _WIN32 #include #endif -namespace i2p -{ -namespace transport -{ - SSUServer::SSUServer (int port): - m_IsRunning(false), m_Thread (nullptr), - m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service), - m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6), - m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port), - m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6), - m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service), - m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service), - m_IsSyncClockFromPeers (true) - { - } +namespace i2p { + namespace transport { + SSUServer::SSUServer(int port) : + m_IsRunning(false), m_Thread(nullptr), + m_ReceiversThread(nullptr), m_ReceiversThreadV6(nullptr), m_Work(m_Service), + m_ReceiversWork(m_ReceiversService), m_ReceiversWorkV6(m_ReceiversServiceV6), + m_Endpoint(boost::asio::ip::udp::v4(), port), m_EndpointV6(boost::asio::ip::udp::v6(), port), + m_Socket(m_ReceiversService), m_SocketV6(m_ReceiversServiceV6), + m_IntroducersUpdateTimer(m_Service), m_IntroducersUpdateTimerV6(m_Service), + m_PeerTestsCleanupTimer(m_Service), m_TerminationTimer(m_Service), m_TerminationTimerV6(m_Service), + m_IsSyncClockFromPeers(true) { + } - SSUServer::~SSUServer () - { - } + SSUServer::~SSUServer() { + } - void SSUServer::OpenSocket () - { - try - { - m_Socket.open (boost::asio::ip::udp::v4()); - m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); - m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); - m_Socket.bind (m_Endpoint); - LogPrint (eLogInfo, "SSU: Start listening v4 port ", m_Endpoint.port()); - } - catch ( std::exception & ex ) - { - LogPrint (eLogError, "SSU: Failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what()); - ThrowFatal ("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); - } - } + void SSUServer::OpenSocket() { + try { + m_Socket.open(boost::asio::ip::udp::v4()); + m_Socket.set_option(boost::asio::socket_base::receive_buffer_size(SSU_SOCKET_RECEIVE_BUFFER_SIZE)); + m_Socket.set_option(boost::asio::socket_base::send_buffer_size(SSU_SOCKET_SEND_BUFFER_SIZE)); + m_Socket.bind(m_Endpoint); + LogPrint(eLogInfo, "SSU: Start listening v4 port ", m_Endpoint.port()); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU: Failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what()); + ThrowFatal("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what()); + } + } - void SSUServer::OpenSocketV6 () - { - try - { - m_SocketV6.open (boost::asio::ip::udp::v6()); - m_SocketV6.set_option (boost::asio::ip::v6_only (true)); - m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); - m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); + void SSUServer::OpenSocketV6() { + try { + m_SocketV6.open(boost::asio::ip::udp::v6()); + m_SocketV6.set_option(boost::asio::ip::v6_only(true)); + m_SocketV6.set_option(boost::asio::socket_base::receive_buffer_size(SSU_SOCKET_RECEIVE_BUFFER_SIZE)); + m_SocketV6.set_option(boost::asio::socket_base::send_buffer_size(SSU_SOCKET_SEND_BUFFER_SIZE)); #if defined(__linux__) && !defined(_NETINET_IN_H) - if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address - { - // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others + if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address + { + // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others #if (BOOST_VERSION >= 105500) - typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; #else - typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; #endif - m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); - } + m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); + } #endif - m_SocketV6.bind (m_EndpointV6); - LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port()); - } - catch ( std::exception & ex ) - { - LogPrint (eLogError, "SSU: Failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what()); - ThrowFatal ("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); - } - } + m_SocketV6.bind(m_EndpointV6); + LogPrint(eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port()); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU: Failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what()); + ThrowFatal("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what()); + } + } - void SSUServer::Start () - { - i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&SSUServer::Run, this)); - if (context.SupportsV4 ()) - { - OpenSocket (); - m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); - m_ReceiversService.post (std::bind (&SSUServer::Receive, this)); - ScheduleTermination (); - ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers - } - if (context.SupportsV6 ()) - { - OpenSocketV6 (); - m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this)); - m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this)); - ScheduleTerminationV6 (); - ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers - } - SchedulePeerTestsCleanupTimer (); - } + void SSUServer::Start() { + i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); + m_IsRunning = true; + m_Thread = new std::thread(std::bind(&SSUServer::Run, this)); + if (context.SupportsV4()) { + OpenSocket(); + m_ReceiversThread = new std::thread(std::bind(&SSUServer::RunReceivers, this)); + m_ReceiversService.post(std::bind(&SSUServer::Receive, this)); + ScheduleTermination(); + ScheduleIntroducersUpdateTimer(); // wait for 30 seconds and decide if we need introducers + } + if (context.SupportsV6()) { + OpenSocketV6(); + m_ReceiversThreadV6 = new std::thread(std::bind(&SSUServer::RunReceiversV6, this)); + m_ReceiversServiceV6.post(std::bind(&SSUServer::ReceiveV6, this)); + ScheduleTerminationV6(); + ScheduleIntroducersUpdateTimerV6(); // wait for 30 seconds and decide if we need introducers + } + SchedulePeerTestsCleanupTimer(); + } - void SSUServer::Stop () - { - DeleteAllSessions (); - m_IsRunning = false; - m_TerminationTimer.cancel (); - m_TerminationTimerV6.cancel (); - m_IntroducersUpdateTimer.cancel (); - m_IntroducersUpdateTimerV6.cancel (); - m_Service.stop (); - m_Socket.close (); - m_SocketV6.close (); - m_ReceiversService.stop (); - m_ReceiversServiceV6.stop (); - if (m_ReceiversThread) - { - m_ReceiversThread->join (); - delete m_ReceiversThread; - m_ReceiversThread = nullptr; - } - if (m_ReceiversThreadV6) - { - m_ReceiversThreadV6->join (); - delete m_ReceiversThreadV6; - m_ReceiversThreadV6 = nullptr; - } - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } + void SSUServer::Stop() { + DeleteAllSessions(); + m_IsRunning = false; + m_TerminationTimer.cancel(); + m_TerminationTimerV6.cancel(); + m_IntroducersUpdateTimer.cancel(); + m_IntroducersUpdateTimerV6.cancel(); + m_Service.stop(); + m_Socket.close(); + m_SocketV6.close(); + m_ReceiversService.stop(); + m_ReceiversServiceV6.stop(); + if (m_ReceiversThread) { + m_ReceiversThread->join(); + delete m_ReceiversThread; + m_ReceiversThread = nullptr; + } + if (m_ReceiversThreadV6) { + m_ReceiversThreadV6->join(); + delete m_ReceiversThreadV6; + m_ReceiversThreadV6 = nullptr; + } + if (m_Thread) { + m_Thread->join(); + delete m_Thread; + m_Thread = nullptr; + } + } - void SSUServer::Run () - { - i2p::util::SetThreadName("SSU"); + void SSUServer::Run() { + i2p::util::SetThreadName("SSU"); - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: Server runtime exception: ", ex.what ()); - } - } - } + while (m_IsRunning) { + try { + m_Service.run(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU: Server runtime exception: ", ex.what()); + } + } + } - void SSUServer::RunReceivers () - { - i2p::util::SetThreadName("SSUv4"); + void SSUServer::RunReceivers() { + i2p::util::SetThreadName("SSUv4"); - while (m_IsRunning) - { - try - { - m_ReceiversService.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: Receivers runtime exception: ", ex.what ()); - if (m_IsRunning) - { - // restart socket - m_Socket.close (); - OpenSocket (); - Receive (); - } - } - } - } + while (m_IsRunning) { + try { + m_ReceiversService.run(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU: Receivers runtime exception: ", ex.what()); + if (m_IsRunning) { + // restart socket + m_Socket.close(); + OpenSocket(); + Receive(); + } + } + } + } - void SSUServer::RunReceiversV6 () - { - i2p::util::SetThreadName("SSUv6"); + void SSUServer::RunReceiversV6() { + i2p::util::SetThreadName("SSUv6"); - while (m_IsRunning) - { - try - { - m_ReceiversServiceV6.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ()); - if (m_IsRunning) - { - m_SocketV6.close (); - OpenSocketV6 (); - ReceiveV6 (); - } - } - } - } + while (m_IsRunning) { + try { + m_ReceiversServiceV6.run(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU: v6 receivers runtime exception: ", ex.what()); + if (m_IsRunning) { + m_SocketV6.close(); + OpenSocketV6(); + ReceiveV6(); + } + } + } + } - void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress) - { - if (localAddress.is_v6 ()) - m_EndpointV6.address (localAddress); - else if (localAddress.is_v4 ()) - m_Endpoint.address (localAddress); - } + void SSUServer::SetLocalAddress(const boost::asio::ip::address &localAddress) { + if (localAddress.is_v6()) + m_EndpointV6.address(localAddress); + else if (localAddress.is_v4()) + m_Endpoint.address(localAddress); + } - void SSUServer::AddRelay (uint32_t tag, std::shared_ptr relay) - { - m_Relays.emplace (tag, relay); - } + void SSUServer::AddRelay(uint32_t tag, std::shared_ptr relay) { + m_Relays.emplace(tag, relay); + } - void SSUServer::RemoveRelay (uint32_t tag) - { - m_Relays.erase (tag); - } + void SSUServer::RemoveRelay(uint32_t tag) { + m_Relays.erase(tag); + } - std::shared_ptr SSUServer::FindRelaySession (uint32_t tag) - { - auto it = m_Relays.find (tag); - if (it != m_Relays.end ()) - { - if (it->second->GetState () == eSessionStateEstablished) - return it->second; - else - m_Relays.erase (it); - } - return nullptr; - } + std::shared_ptr SSUServer::FindRelaySession(uint32_t tag) { + auto it = m_Relays.find(tag); + if (it != m_Relays.end()) { + if (it->second->GetState() == eSessionStateEstablished) + return it->second; + else + m_Relays.erase(it); + } + return nullptr; + } - void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) - { - boost::system::error_code ec; - if (to.protocol () == boost::asio::ip::udp::v4()) - m_Socket.send_to (boost::asio::buffer (buf, len), to, 0, ec); - else - m_SocketV6.send_to (boost::asio::buffer (buf, len), to, 0, ec); + void SSUServer::Send(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &to) { + boost::system::error_code ec; + if (to.protocol() == boost::asio::ip::udp::v4()) + m_Socket.send_to(boost::asio::buffer(buf, len), to, 0, ec); + else + m_SocketV6.send_to(boost::asio::buffer(buf, len), to, 0, ec); - if (ec) - { - LogPrint (eLogError, "SSU: Send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")"); - } - } + if (ec) { + LogPrint(eLogError, "SSU: Send exception: ", ec.message(), " while trying to send data to ", + to.address(), ":", to.port(), " (length: ", len, ")"); + } + } - void SSUServer::Receive () - { - SSUPacket * packet = m_PacketsPool.AcquireMt (); - m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, - std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet)); - } + void SSUServer::Receive() { + SSUPacket *packet = m_PacketsPool.AcquireMt(); + m_Socket.async_receive_from(boost::asio::buffer(packet->buf, SSU_MTU_V4), packet->from, + std::bind(&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, + std::placeholders::_2, packet)); + } - void SSUServer::ReceiveV6 () - { - SSUPacket * packet = m_PacketsPool.AcquireMt (); - m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, - std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet)); - } + void SSUServer::ReceiveV6() { + SSUPacket *packet = m_PacketsPool.AcquireMt(); + m_SocketV6.async_receive_from(boost::asio::buffer(packet->buf, SSU_MTU_V6), packet->from, + std::bind(&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, + std::placeholders::_2, packet)); + } - void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) - { - if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable + void SSUServer::HandleReceivedFrom(const boost::system::error_code &ecode, std::size_t bytes_transferred, + SSUPacket *packet) { + if (!ecode + || ecode == boost::asio::error::connection_refused + || ecode == boost::asio::error::connection_reset + || ecode == boost::asio::error::network_unreachable + || ecode == boost::asio::error::host_unreachable #ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ + || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ #endif - ) - // just try continue reading when received ICMP response otherwise socket can crash, - // but better to find out which host were sent it and mark that router as unreachable - { - packet->len = bytes_transferred; - std::vector packets; - packets.push_back (packet); + ) + // just try continue reading when received ICMP response otherwise socket can crash, + // but better to find out which host were sent it and mark that router as unreachable + { + packet->len = bytes_transferred; + std::vector packets; + packets.push_back(packet); - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - if (!ec) - { - while (moreBytes && packets.size () < 25) - { - packet = m_PacketsPool.AcquireMt (); - packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec); - if (!ec) - { - packets.push_back (packet); - moreBytes = m_Socket.available(ec); - if (ec) break; - } - else - { - LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ()); - m_PacketsPool.ReleaseMt (packet); - break; - } - } - } + boost::system::error_code ec; + size_t moreBytes = m_Socket.available(ec); + if (!ec) { + while (moreBytes && packets.size() < 25) { + packet = m_PacketsPool.AcquireMt(); + packet->len = m_Socket.receive_from(boost::asio::buffer(packet->buf, SSU_MTU_V4), packet->from, + 0, ec); + if (!ec) { + packets.push_back(packet); + moreBytes = m_Socket.available(ec); + if (ec) break; + } else { + LogPrint(eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message()); + m_PacketsPool.ReleaseMt(packet); + break; + } + } + } - m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions)); - Receive (); - } - else - { - m_PacketsPool.ReleaseMt (packet); - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "SSU: Receive error: code ", ecode.value(), ": ", ecode.message ()); - m_Socket.close (); - OpenSocket (); - Receive (); - } - } - } + m_Service.post(std::bind(&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions)); + Receive(); + } else { + m_PacketsPool.ReleaseMt(packet); + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogError, "SSU: Receive error: code ", ecode.value(), ": ", ecode.message()); + m_Socket.close(); + OpenSocket(); + Receive(); + } + } + } - void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) - { - if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable + void SSUServer::HandleReceivedFromV6(const boost::system::error_code &ecode, std::size_t bytes_transferred, + SSUPacket *packet) { + if (!ecode + || ecode == boost::asio::error::connection_refused + || ecode == boost::asio::error::connection_reset + || ecode == boost::asio::error::network_unreachable + || ecode == boost::asio::error::host_unreachable #ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ + || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ #endif - ) - // just try continue reading when received ICMP response otherwise socket can crash, - // but better to find out which host were sent it and mark that router as unreachable - { - packet->len = bytes_transferred; - std::vector packets; - packets.push_back (packet); + ) + // just try continue reading when received ICMP response otherwise socket can crash, + // but better to find out which host were sent it and mark that router as unreachable + { + packet->len = bytes_transferred; + std::vector packets; + packets.push_back(packet); - boost::system::error_code ec; - size_t moreBytes = m_SocketV6.available (ec); - if (!ec) - { - while (moreBytes && packets.size () < 25) - { - packet = m_PacketsPool.AcquireMt (); - packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec); - if (!ec) - { - packets.push_back (packet); - moreBytes = m_SocketV6.available(ec); - if (ec) break; - } - else - { - LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ()); - m_PacketsPool.ReleaseMt (packet);; - break; - } - } - } + boost::system::error_code ec; + size_t moreBytes = m_SocketV6.available(ec); + if (!ec) { + while (moreBytes && packets.size() < 25) { + packet = m_PacketsPool.AcquireMt(); + packet->len = m_SocketV6.receive_from(boost::asio::buffer(packet->buf, SSU_MTU_V6), + packet->from, 0, ec); + if (!ec) { + packets.push_back(packet); + moreBytes = m_SocketV6.available(ec); + if (ec) break; + } else { + LogPrint(eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message()); + m_PacketsPool.ReleaseMt(packet);; + break; + } + } + } - m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6)); - ReceiveV6 (); - } - else - { - m_PacketsPool.ReleaseMt (packet); - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ()); - m_SocketV6.close (); - OpenSocketV6 (); - ReceiveV6 (); - } - } - } + m_Service.post(std::bind(&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6)); + ReceiveV6(); + } else { + m_PacketsPool.ReleaseMt(packet); + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message()); + m_SocketV6.close(); + OpenSocketV6(); + ReceiveV6(); + } + } + } - void SSUServer::HandleReceivedPackets (std::vector packets, - std::map > * sessions) - { - if (!m_IsRunning) return; - std::shared_ptr session; - for (auto& packet: packets) - { - try - { - if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous - { - if (session) - { - session->FlushData (); - session = nullptr; - } - auto it = sessions->find (packet->from); - if (it != sessions->end ()) - session = it->second; - if (!session && packet->len > 0) - { - session = std::make_shared (*this, packet->from); - session->WaitForConnect (); - (*sessions)[packet->from] = session; - LogPrint (eLogDebug, "SSU: New session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); - } - } - if (session) - session->ProcessNextMessage (packet->buf, packet->len, packet->from); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ()); - if (session) session->FlushData (); - session = nullptr; - } - } - m_PacketsPool.ReleaseMt (packets); - if (session) session->FlushData (); - } + void SSUServer::HandleReceivedPackets(std::vector packets, + std::map > *sessions) { + if (!m_IsRunning) return; + std::shared_ptr session; + for (auto &packet: packets) { + try { + if (!session || session->GetRemoteEndpoint() != + packet->from) // we received packet for other session than previous + { + if (session) { + session->FlushData(); + session = nullptr; + } + auto it = sessions->find(packet->from); + if (it != sessions->end()) + session = it->second; + if (!session && packet->len > 0) { + session = std::make_shared(*this, packet->from); + session->WaitForConnect(); + (*sessions)[packet->from] = session; + LogPrint(eLogDebug, "SSU: New session from ", packet->from.address().to_string(), ":", + packet->from.port(), " created"); + } + } + if (session) + session->ProcessNextMessage(packet->buf, packet->len, packet->from); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU: HandleReceivedPackets ", ex.what()); + if (session) session->FlushData(); + session = nullptr; + } + } + m_PacketsPool.ReleaseMt(packets); + if (session) session->FlushData(); + } - std::shared_ptr SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const - { - auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions; - auto it = sessions.find (e); - if (it != sessions.end ()) - return it->second; - else - return nullptr; - } + std::shared_ptr SSUServer::FindSession(const boost::asio::ip::udp::endpoint &e) const { + auto &sessions = e.address().is_v6() ? m_SessionsV6 : m_Sessions; + auto it = sessions.find(e); + if (it != sessions.end()) + return it->second; + else + return nullptr; + } - bool SSUServer::CreateSession (std::shared_ptr router, bool peerTest, bool v4only) - { - auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ()); - if (address) - return CreateSession (router, address, peerTest); - else - LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address"); - return false; - } + bool SSUServer::CreateSession(std::shared_ptr router, bool peerTest, bool v4only) { + auto address = router->GetSSUAddress(v4only || !context.SupportsV6()); + if (address) + return CreateSession(router, address, peerTest); + else + LogPrint(eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation(router->GetIdentHash()), + " doesn't have SSU address"); + return false; + } - bool SSUServer::CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest) - { - if (router && address) - { - if (address->UsesIntroducer ()) - m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread - else - { - if (address->host.is_unspecified () || !address->port) return false; - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest)); - } - } - else - return false; - return true; - } + bool SSUServer::CreateSession(std::shared_ptr router, + std::shared_ptr address, bool peerTest) { + if (router && address) { + if (address->UsesIntroducer()) + m_Service.post(std::bind(&SSUServer::CreateSessionThroughIntroducer, this, router, address, + peerTest)); // always V4 thread + else { + if (address->host.is_unspecified() || !address->port) return false; + boost::asio::ip::udp::endpoint remoteEndpoint(address->host, address->port); + m_Service.post(std::bind(&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest)); + } + } else + return false; + return true; + } - void SSUServer::CreateDirectSession (std::shared_ptr router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest) - { - auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions; - auto it = sessions.find (remoteEndpoint); - if (it != sessions.end ()) - { - auto session = it->second; - if (peerTest && session->GetState () == eSessionStateEstablished) - session->SendPeerTest (); - } - else - { - // otherwise create new session - auto session = std::make_shared (*this, remoteEndpoint, router, peerTest); - sessions[remoteEndpoint] = session; + void SSUServer::CreateDirectSession(std::shared_ptr router, + boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest) { + auto &sessions = remoteEndpoint.address().is_v6() ? m_SessionsV6 : m_Sessions; + auto it = sessions.find(remoteEndpoint); + if (it != sessions.end()) { + auto session = it->second; + if (peerTest && session->GetState() == eSessionStateEstablished) + session->SendPeerTest(); + } else { + // otherwise create new session + auto session = std::make_shared(*this, remoteEndpoint, router, peerTest); + sessions[remoteEndpoint] = session; - // connect - LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ", - remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); - session->Connect (); - } - } + // connect + LogPrint(eLogDebug, "SSU: Creating new session to [", + i2p::data::GetIdentHashAbbreviation(router->GetIdentHash()), "] ", + remoteEndpoint.address().to_string(), ":", remoteEndpoint.port()); + session->Connect(); + } + } - void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr router, - std::shared_ptr address, bool peerTest) - { - if (router && address && address->UsesIntroducer ()) - { - if (address->IsV4 () && !i2p::context.SupportsV4 ()) return; - if (address->IsV6 () && !i2p::context.SupportsV6 ()) return; - if (!address->host.is_unspecified () && address->port) - { - // we rarely come here - auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions; - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - auto it = sessions.find (remoteEndpoint); - // check if session is presented already - if (it != sessions.end ()) - { - auto session = it->second; - if (peerTest && session->GetState () == eSessionStateEstablished) - session->SendPeerTest (); - return; - } - } - // create new session - int numIntroducers = address->ssu->introducers.size (); - if (numIntroducers > 0) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::shared_ptr introducerSession; - const i2p::data::RouterInfo::Introducer * introducer = nullptr; - // we might have a session to introducer already - auto offset = rand (); - for (int i = 0; i < numIntroducers; i++) - { - auto intr = &(address->ssu->introducers[(offset + i)%numIntroducers]); - if (!intr->iPort) continue; // skip invalid introducer - if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer - boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort); - if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4 - { - if (!introducer) introducer = intr; - auto it = m_Sessions.find (ep); - if (it != m_Sessions.end ()) - { - introducerSession = it->second; - break; - } - } - if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6 - { - if (!introducer) introducer = intr; - auto it = m_SessionsV6.find (ep); - if (it != m_SessionsV6.end ()) - { - introducerSession = it->second; - break; - } - } - } - if (!introducer) - { - LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented"); - return; - } + void SSUServer::CreateSessionThroughIntroducer(std::shared_ptr router, + std::shared_ptr address, + bool peerTest) { + if (router && address && address->UsesIntroducer()) { + if (address->IsV4() && !i2p::context.SupportsV4()) return; + if (address->IsV6() && !i2p::context.SupportsV6()) return; + if (!address->host.is_unspecified() && address->port) { + // we rarely come here + auto &sessions = address->host.is_v6() ? m_SessionsV6 : m_Sessions; + boost::asio::ip::udp::endpoint remoteEndpoint(address->host, address->port); + auto it = sessions.find(remoteEndpoint); + // check if session is presented already + if (it != sessions.end()) { + auto session = it->second; + if (peerTest && session->GetState() == eSessionStateEstablished) + session->SendPeerTest(); + return; + } + } + // create new session + int numIntroducers = address->ssu->introducers.size(); + if (numIntroducers > 0) { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + std::shared_ptr introducerSession; + const i2p::data::RouterInfo::Introducer *introducer = nullptr; + // we might have a session to introducer already + auto offset = rand(); + for (int i = 0; i < numIntroducers; i++) { + auto intr = &(address->ssu->introducers[(offset + i) % numIntroducers]); + if (!intr->iPort) continue; // skip invalid introducer + if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer + boost::asio::ip::udp::endpoint ep(intr->iHost, intr->iPort); + if (ep.address().is_v4() && address->IsV4()) // ipv4 + { + if (!introducer) introducer = intr; + auto it = m_Sessions.find(ep); + if (it != m_Sessions.end()) { + introducerSession = it->second; + break; + } + } + if (ep.address().is_v6() && address->IsV6()) // ipv6 + { + if (!introducer) introducer = intr; + auto it = m_SessionsV6.find(ep); + if (it != m_SessionsV6.end()) { + introducerSession = it->second; + break; + } + } + } + if (!introducer) { + LogPrint(eLogWarning, + "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented"); + return; + } - if (introducerSession) // session found - LogPrint (eLogWarning, "SSU: Session to introducer already exists"); - else // create new - { - LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost); - boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort); - introducerSession = std::make_shared (*this, introducerEndpoint, router); - if (introducerEndpoint.address ().is_v4 ()) - m_Sessions[introducerEndpoint] = introducerSession; - else if (introducerEndpoint.address ().is_v6 ()) - m_SessionsV6[introducerEndpoint] = introducerSession; - } - if (!address->host.is_unspecified () && address->port) - { - // create session - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - auto session = std::make_shared (*this, remoteEndpoint, router, peerTest); - if (address->host.is_v4 ()) - m_Sessions[remoteEndpoint] = session; - else if (address->host.is_v6 ()) - m_SessionsV6[remoteEndpoint] = session; + if (introducerSession) // session found + LogPrint(eLogWarning, "SSU: Session to introducer already exists"); + else // create new + { + LogPrint(eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost); + boost::asio::ip::udp::endpoint introducerEndpoint(introducer->iHost, introducer->iPort); + introducerSession = std::make_shared(*this, introducerEndpoint, router); + if (introducerEndpoint.address().is_v4()) + m_Sessions[introducerEndpoint] = introducerSession; + else if (introducerEndpoint.address().is_v6()) + m_SessionsV6[introducerEndpoint] = introducerSession; + } + if (!address->host.is_unspecified() && address->port) { + // create session + boost::asio::ip::udp::endpoint remoteEndpoint(address->host, address->port); + auto session = std::make_shared(*this, remoteEndpoint, router, peerTest); + if (address->host.is_v4()) + m_Sessions[remoteEndpoint] = session; + else if (address->host.is_v6()) + m_SessionsV6[remoteEndpoint] = session; - // introduce - LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), - "] through introducer ", introducer->iHost, ":", introducer->iPort); - session->WaitForIntroduction (); - if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) - { - uint8_t buf[1]; - Send (buf, 0, remoteEndpoint); // send HolePunch - } - } - introducerSession->Introduce (*introducer, router); - } - else - LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present"); - } - } + // introduce + LogPrint(eLogInfo, "SSU: Introduce new session to [", + i2p::data::GetIdentHashAbbreviation(router->GetIdentHash()), + "] through introducer ", introducer->iHost, ":", introducer->iPort); + session->WaitForIntroduction(); + if ((address->host.is_v4() && i2p::context.GetStatus() == eRouterStatusFirewalled) || + (address->host.is_v6() && i2p::context.GetStatusV6() == eRouterStatusFirewalled)) { + uint8_t buf[1]; + Send(buf, 0, remoteEndpoint); // send HolePunch + } + } + introducerSession->Introduce(*introducer, router); + } else + LogPrint(eLogWarning, "SSU: Can't connect to unreachable router and no introducers present"); + } + } - void SSUServer::DeleteSession (std::shared_ptr session) - { - if (session) - { - session->Close (); - auto& ep = session->GetRemoteEndpoint (); - if (ep.address ().is_v6 ()) - m_SessionsV6.erase (ep); - else - m_Sessions.erase (ep); - } - } + void SSUServer::DeleteSession(std::shared_ptr session) { + if (session) { + session->Close(); + auto &ep = session->GetRemoteEndpoint(); + if (ep.address().is_v6()) + m_SessionsV6.erase(ep); + else + m_Sessions.erase(ep); + } + } - void SSUServer::DeleteAllSessions () - { - for (auto& it: m_Sessions) - it.second->Close (); - m_Sessions.clear (); + void SSUServer::DeleteAllSessions() { + for (auto &it: m_Sessions) + it.second->Close(); + m_Sessions.clear(); - for (auto& it: m_SessionsV6) - it.second->Close (); - m_SessionsV6.clear (); - } + for (auto &it: m_SessionsV6) + it.second->Close(); + m_SessionsV6.clear(); + } - template - std::shared_ptr SSUServer::GetRandomV4Session (Filter filter) // v4 only - { - std::vector > filteredSessions; - for (const auto& s :m_Sessions) - if (filter (s.second)) filteredSessions.push_back (s.second); - if (filteredSessions.size () > 0) - { - auto ind = rand () % filteredSessions.size (); - return filteredSessions[ind]; - } - return nullptr; - } + template + std::shared_ptr SSUServer::GetRandomV4Session(Filter filter) // v4 only + { + std::vector > filteredSessions; + for (const auto &s: m_Sessions) + if (filter(s.second)) filteredSessions.push_back(s.second); + if (filteredSessions.size() > 0) { + auto ind = rand() % filteredSessions.size(); + return filteredSessions[ind]; + } + return nullptr; + } - std::shared_ptr SSUServer::GetRandomEstablishedV4Session (std::shared_ptr excluded) // v4 only - { - return GetRandomV4Session ( - [excluded](std::shared_ptr session)->bool - { - return session->GetState () == eSessionStateEstablished && session != excluded; - } - ); - } + std::shared_ptr + SSUServer::GetRandomEstablishedV4Session(std::shared_ptr excluded) // v4 only + { + return GetRandomV4Session( + [excluded](std::shared_ptr session) -> bool { + return session->GetState() == eSessionStateEstablished && session != excluded; + } + ); + } - template - std::shared_ptr SSUServer::GetRandomV6Session (Filter filter) // v6 only - { - std::vector > filteredSessions; - for (const auto& s :m_SessionsV6) - if (filter (s.second)) filteredSessions.push_back (s.second); - if (filteredSessions.size () > 0) - { - auto ind = rand () % filteredSessions.size (); - return filteredSessions[ind]; - } - return nullptr; - } + template + std::shared_ptr SSUServer::GetRandomV6Session(Filter filter) // v6 only + { + std::vector > filteredSessions; + for (const auto &s: m_SessionsV6) + if (filter(s.second)) filteredSessions.push_back(s.second); + if (filteredSessions.size() > 0) { + auto ind = rand() % filteredSessions.size(); + return filteredSessions[ind]; + } + return nullptr; + } - std::shared_ptr SSUServer::GetRandomEstablishedV6Session (std::shared_ptr excluded) // v6 only - { - return GetRandomV6Session ( - [excluded](std::shared_ptr session)->bool - { - return session->GetState () == eSessionStateEstablished && session != excluded; - } - ); - } + std::shared_ptr + SSUServer::GetRandomEstablishedV6Session(std::shared_ptr excluded) // v6 only + { + return GetRandomV6Session( + [excluded](std::shared_ptr session) -> bool { + return session->GetState() == eSessionStateEstablished && session != excluded; + } + ); + } - std::list > SSUServer::FindIntroducers (int maxNumIntroducers, - bool v4, std::set& excluded) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::list > ret; - const auto& sessions = v4 ? m_Sessions : m_SessionsV6; - for (const auto& s : sessions) - { - if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished && - ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) - ret.push_back (s.second); - else if (s.second->GetRemoteIdentity ()) - excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ()); - } - if ((int)ret.size () > maxNumIntroducers) - { - // shink ret randomly - int sz = ret.size () - maxNumIntroducers; - for (int i = 0; i < sz; i++) - { - auto ind = rand () % ret.size (); - auto it = ret.begin (); - std::advance (it, ind); - ret.erase (it); - } - } - return ret; - } + std::list > SSUServer::FindIntroducers(int maxNumIntroducers, + bool v4, + std::set &excluded) { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + std::list > ret; + const auto &sessions = v4 ? m_Sessions : m_SessionsV6; + for (const auto &s: sessions) { + if (s.second->GetRelayTag() && s.second->GetState() == eSessionStateEstablished && + ts < s.second->GetCreationTime() + SSU_TO_INTRODUCER_SESSION_EXPIRATION) + ret.push_back(s.second); + else if (s.second->GetRemoteIdentity()) + excluded.insert(s.second->GetRemoteIdentity()->GetIdentHash()); + } + if ((int) ret.size() > maxNumIntroducers) { + // shink ret randomly + int sz = ret.size() - maxNumIntroducers; + for (int i = 0; i < sz; i++) { + auto ind = rand() % ret.size(); + auto it = ret.begin(); + std::advance(it, ind); + ret.erase(it); + } + } + return ret; + } - void SSUServer::RescheduleIntroducersUpdateTimer () - { - m_IntroducersUpdateTimer.cancel (); - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } + void SSUServer::RescheduleIntroducersUpdateTimer() { + m_IntroducersUpdateTimer.cancel(); + m_IntroducersUpdateTimer.expires_from_now(boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL / 2)); + m_IntroducersUpdateTimer.async_wait(std::bind(&SSUServer::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, true)); + } - void SSUServer::ScheduleIntroducersUpdateTimer () - { - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } + void SSUServer::ScheduleIntroducersUpdateTimer() { + m_IntroducersUpdateTimer.expires_from_now(boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimer.async_wait(std::bind(&SSUServer::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, true)); + } - void SSUServer::RescheduleIntroducersUpdateTimerV6 () - { - m_IntroducersUpdateTimerV6.cancel (); - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } + void SSUServer::RescheduleIntroducersUpdateTimerV6() { + m_IntroducersUpdateTimerV6.cancel(); + m_IntroducersUpdateTimerV6.expires_from_now(boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL / 2)); + m_IntroducersUpdateTimerV6.async_wait(std::bind(&SSUServer::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, false)); + } - void SSUServer::ScheduleIntroducersUpdateTimerV6 () - { - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } + void SSUServer::ScheduleIntroducersUpdateTimerV6() { + m_IntroducersUpdateTimerV6.expires_from_now(boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimerV6.async_wait(std::bind(&SSUServer::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, false)); + } - void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4) - { - if (ecode != boost::asio::error::operation_aborted) - { - // timeout expired - if (v4) - { - if (i2p::context.GetStatus () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimer (); - return; - } - if (i2p::context.GetStatus () != eRouterStatusFirewalled) - { - // we don't need introducers - m_Introducers.clear (); - return; - } - // we are firewalled - if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4 - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimerV6 (); - return; - } - if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled) - { - // we don't need introducers - m_IntroducersV6.clear (); - return; - } - // we are firewalled - auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address (); - if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachable (false, true); // v6 - } + void SSUServer::HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4) { + if (ecode != boost::asio::error::operation_aborted) { + // timeout expired + if (v4) { + if (i2p::context.GetStatus() == eRouterStatusTesting) { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimer(); + return; + } + if (i2p::context.GetStatus() != eRouterStatusFirewalled) { + // we don't need introducers + m_Introducers.clear(); + return; + } + // we are firewalled + if (!i2p::context.IsUnreachable()) i2p::context.SetUnreachable(true, false); // v4 + } else { + if (i2p::context.GetStatusV6() == eRouterStatusTesting) { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimerV6(); + return; + } + if (i2p::context.GetStatusV6() != eRouterStatusFirewalled) { + // we don't need introducers + m_IntroducersV6.clear(); + return; + } + // we are firewalled + auto addr = i2p::context.GetRouterInfo().GetSSUV6Address(); + if (addr && addr->ssu && addr->ssu->introducers.empty()) + i2p::context.SetUnreachable(false, true); // v6 + } - std::list newList; - size_t numIntroducers = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::set excluded; - auto& introducers = v4 ? m_Introducers : m_IntroducersV6; - for (const auto& it : introducers) - { - auto session = FindSession (it); - if (session) - { - if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) - session->SendKeepAlive (); - if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION) - { - newList.push_back (it); - numIntroducers++; - if (session->GetRemoteIdentity ()) - excluded.insert (session->GetRemoteIdentity ()->GetIdentHash ()); - } - else - session = nullptr; - } - if (!session) - i2p::context.RemoveIntroducer (it); - } - if (numIntroducers < SSU_MAX_NUM_INTRODUCERS) - { - // create new - auto sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates - if (sessions.empty () && !introducers.empty ()) - { - // bump creation time for previous introducers if no new sessions found - LogPrint (eLogDebug, "SSU: No new introducers found. Trying to reuse existing"); - for (const auto& it : introducers) - { - auto session = FindSession (it); - if (session) - session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION); - } - // try again - excluded.clear (); - sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); - } - for (const auto& it1: sessions) - { - const auto& ep = it1->GetRemoteEndpoint (); - i2p::data::RouterInfo::Introducer introducer; - introducer.iHost = ep.address (); - introducer.iPort = ep.port (); - introducer.iTag = it1->GetRelayTag (); - introducer.iKey = it1->GetIntroKey (); - introducer.iExp = it1->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION; - if (i2p::context.AddIntroducer (introducer)) - { - newList.push_back (ep); - if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break; - } - if (it1->GetRemoteIdentity ()) - excluded.insert (it1->GetRemoteIdentity ()->GetIdentHash ()); - } - } - introducers = newList; - if (introducers.size () < SSU_MAX_NUM_INTRODUCERS) - { - for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++) - { - auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded); - if (introducer) - { - auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address (); - if (address && !address->host.is_unspecified () && address->port) - { - boost::asio::ip::udp::endpoint ep (address->host, address->port); - if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet - { - CreateDirectSession (introducer, ep, false); - excluded.insert (introducer->GetIdentHash ()); - } - } - } - else - { - LogPrint (eLogDebug, "SSU: Can't find more introducers"); - break; - } - } - } - if (v4) - ScheduleIntroducersUpdateTimer (); - else - ScheduleIntroducersUpdateTimerV6 (); - } - } + std::list newList; + size_t numIntroducers = 0; + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + std::set excluded; + auto &introducers = v4 ? m_Introducers : m_IntroducersV6; + for (const auto &it: introducers) { + auto session = FindSession(it); + if (session) { + if (ts < session->GetCreationTime() + SSU_TO_INTRODUCER_SESSION_EXPIRATION) + session->SendKeepAlive(); + if (ts < session->GetCreationTime() + SSU_TO_INTRODUCER_SESSION_DURATION) { + newList.push_back(it); + numIntroducers++; + if (session->GetRemoteIdentity()) + excluded.insert(session->GetRemoteIdentity()->GetIdentHash()); + } else + session = nullptr; + } + if (!session) + i2p::context.RemoveIntroducer(it); + } + if (numIntroducers < SSU_MAX_NUM_INTRODUCERS) { + // create new + auto sessions = FindIntroducers(SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates + if (sessions.empty() && !introducers.empty()) { + // bump creation time for previous introducers if no new sessions found + LogPrint(eLogDebug, "SSU: No new introducers found. Trying to reuse existing"); + for (const auto &it: introducers) { + auto session = FindSession(it); + if (session) + session->SetCreationTime( + session->GetCreationTime() + SSU_TO_INTRODUCER_SESSION_DURATION); + } + // try again + excluded.clear(); + sessions = FindIntroducers(SSU_MAX_NUM_INTRODUCERS, v4, excluded); + } + for (const auto &it1: sessions) { + const auto &ep = it1->GetRemoteEndpoint(); + i2p::data::RouterInfo::Introducer introducer; + introducer.iHost = ep.address(); + introducer.iPort = ep.port(); + introducer.iTag = it1->GetRelayTag(); + introducer.iKey = it1->GetIntroKey(); + introducer.iExp = it1->GetCreationTime() + SSU_TO_INTRODUCER_SESSION_EXPIRATION; + if (i2p::context.AddIntroducer(introducer)) { + newList.push_back(ep); + if (newList.size() >= SSU_MAX_NUM_INTRODUCERS) break; + } + if (it1->GetRemoteIdentity()) + excluded.insert(it1->GetRemoteIdentity()->GetIdentHash()); + } + } + introducers = newList; + if (introducers.size() < SSU_MAX_NUM_INTRODUCERS) { + for (auto i = introducers.size(); i < SSU_MAX_NUM_INTRODUCERS; i++) { + auto introducer = i2p::data::netdb.GetRandomIntroducer(v4, excluded); + if (introducer) { + auto address = v4 ? introducer->GetSSUAddress(true) : introducer->GetSSUV6Address(); + if (address && !address->host.is_unspecified() && address->port) { + boost::asio::ip::udp::endpoint ep(address->host, address->port); + if (std::find(introducers.begin(), introducers.end(), ep) == + introducers.end()) // not connected yet + { + CreateDirectSession(introducer, ep, false); + excluded.insert(introducer->GetIdentHash()); + } + } + } else { + LogPrint(eLogDebug, "SSU: Can't find more introducers"); + break; + } + } + } + if (v4) + ScheduleIntroducersUpdateTimer(); + else + ScheduleIntroducersUpdateTimerV6(); + } + } - void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session) - { - m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session }; - } + void SSUServer::NewPeerTest(uint32_t nonce, PeerTestParticipant role, std::shared_ptr session) { + m_PeerTests[nonce] = {i2p::util::GetMillisecondsSinceEpoch(), role, session}; + } - PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - return it->second.role; - else - return ePeerTestParticipantUnknown; - } + PeerTestParticipant SSUServer::GetPeerTestParticipant(uint32_t nonce) { + auto it = m_PeerTests.find(nonce); + if (it != m_PeerTests.end()) + return it->second.role; + else + return ePeerTestParticipantUnknown; + } - std::shared_ptr SSUServer::GetPeerTestSession (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - return it->second.session; - else - return nullptr; - } + std::shared_ptr SSUServer::GetPeerTestSession(uint32_t nonce) { + auto it = m_PeerTests.find(nonce); + if (it != m_PeerTests.end()) + return it->second.session; + else + return nullptr; + } - void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - it->second.role = role; - } + void SSUServer::UpdatePeerTest(uint32_t nonce, PeerTestParticipant role) { + auto it = m_PeerTests.find(nonce); + if (it != m_PeerTests.end()) + it->second.role = role; + } - void SSUServer::RemovePeerTest (uint32_t nonce) - { - m_PeerTests.erase (nonce); - } + void SSUServer::RemovePeerTest(uint32_t nonce) { + m_PeerTests.erase(nonce); + } - void SSUServer::SchedulePeerTestsCleanupTimer () - { - m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT)); - m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer, - this, std::placeholders::_1)); - } + void SSUServer::SchedulePeerTestsCleanupTimer() { + m_PeerTestsCleanupTimer.expires_from_now(boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT)); + m_PeerTestsCleanupTimer.async_wait(std::bind(&SSUServer::HandlePeerTestsCleanupTimer, + this, std::placeholders::_1)); + } - void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - int numDeleted = 0; - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) - { - if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL) - { - numDeleted++; - it = m_PeerTests.erase (it); - } - else - ++it; - } - if (numDeleted > 0) - LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired"); - // some cleaups. TODO: use separate timer - m_FragmentsPool.CleanUp (); - m_IncompleteMessagesPool.CleanUp (); - m_SentMessagesPool.CleanUp (); + void SSUServer::HandlePeerTestsCleanupTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + int numDeleted = 0; + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + for (auto it = m_PeerTests.begin(); it != m_PeerTests.end();) { + if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT * 1000LL) { + numDeleted++; + it = m_PeerTests.erase(it); + } else + ++it; + } + if (numDeleted > 0) + LogPrint(eLogDebug, "SSU: ", numDeleted, " peer tests have been expired"); + // some cleaups. TODO: use separate timer + m_FragmentsPool.CleanUp(); + m_IncompleteMessagesPool.CleanUp(); + m_SentMessagesPool.CleanUp(); - SchedulePeerTestsCleanupTimer (); - } - } + SchedulePeerTestsCleanupTimer(); + } + } - void SSUServer::ScheduleTermination () - { - uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5; - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(timeout)); - m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer, - this, std::placeholders::_1)); - } + void SSUServer::ScheduleTermination() { + uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand() % SSU_TERMINATION_CHECK_TIMEOUT) / 5; + m_TerminationTimer.expires_from_now(boost::posix_time::seconds(timeout)); + m_TerminationTimer.async_wait(std::bind(&SSUServer::HandleTerminationTimer, + this, std::placeholders::_1)); + } - void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto& it: m_Sessions) - if (it.second->IsTerminationTimeoutExpired (ts)) - { - auto session = it.second; - if (it.first != session->GetRemoteEndpoint ()) - LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted"); - m_Service.post ([session] - { - LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); - session->Failed (); - }); - } - else - it.second->CleanUp (ts); - ScheduleTermination (); - } - } + void SSUServer::HandleTerminationTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ts = i2p::util::GetSecondsSinceEpoch(); + for (auto &it: m_Sessions) + if (it.second->IsTerminationTimeoutExpired(ts)) { + auto session = it.second; + if (it.first != session->GetRemoteEndpoint()) + LogPrint(eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint(), + " doesn't match key ", it.first, " adjusted"); + m_Service.post([session] { + LogPrint(eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint(), " for ", + session->GetTerminationTimeout(), " seconds"); + session->Failed(); + }); + } else + it.second->CleanUp(ts); + ScheduleTermination(); + } + } - void SSUServer::ScheduleTerminationV6 () - { - uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5; - m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(timeout)); - m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6, - this, std::placeholders::_1)); - } + void SSUServer::ScheduleTerminationV6() { + uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand() % SSU_TERMINATION_CHECK_TIMEOUT) / 5; + m_TerminationTimerV6.expires_from_now(boost::posix_time::seconds(timeout)); + m_TerminationTimerV6.async_wait(std::bind(&SSUServer::HandleTerminationTimerV6, + this, std::placeholders::_1)); + } - void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto& it: m_SessionsV6) - if (it.second->IsTerminationTimeoutExpired (ts)) - { - auto session = it.second; - if (it.first != session->GetRemoteEndpoint ()) - LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first); - m_Service.post ([session] - { - LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); - session->Failed (); - }); - } - else - it.second->CleanUp (ts); - ScheduleTerminationV6 (); - } - } -} + void SSUServer::HandleTerminationTimerV6(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ts = i2p::util::GetSecondsSinceEpoch(); + for (auto &it: m_SessionsV6) + if (it.second->IsTerminationTimeoutExpired(ts)) { + auto session = it.second; + if (it.first != session->GetRemoteEndpoint()) + LogPrint(eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint(), + " doesn't match key ", it.first); + m_Service.post([session] { + LogPrint(eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint(), " for ", + session->GetTerminationTimeout(), " seconds"); + session->Failed(); + }); + } else + it.second->CleanUp(ts); + ScheduleTerminationV6(); + } + } + } } diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h index 25ce4d40..54b9898c 100644 --- a/libi2pd/SSU.h +++ b/libi2pd/SSU.h @@ -25,135 +25,185 @@ #include "I2NPProtocol.h" #include "SSUSession.h" -namespace i2p -{ -namespace transport -{ - const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds - const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds - const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour - const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes - const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds - const size_t SSU_MAX_NUM_INTRODUCERS = 3; - const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K - const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K +namespace i2p { + namespace transport { + const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds + const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds + const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour + const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes + const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds + const size_t SSU_MAX_NUM_INTRODUCERS = 3; + const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K - struct SSUPacket - { - i2p::crypto::AESAlignedBuffer buf; // max MTU + iv + size - boost::asio::ip::udp::endpoint from; - size_t len; - }; + struct SSUPacket { + i2p::crypto::AESAlignedBuffer buf; // max MTU + iv + size + boost::asio::ip::udp::endpoint from; + size_t len; + }; - class SSUServer - { - public: + class SSUServer { + public: - SSUServer (int port); - ~SSUServer (); - void Start (); - void Stop (); - bool CreateSession (std::shared_ptr router, bool peerTest = false, bool v4only = false); - bool CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest = false); - void CreateDirectSession (std::shared_ptr router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest); - std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const; - std::shared_ptr GetRandomEstablishedV4Session (std::shared_ptr excluded); - std::shared_ptr GetRandomEstablishedV6Session (std::shared_ptr excluded); - void DeleteSession (std::shared_ptr session); - void DeleteAllSessions (); + SSUServer(int port); - boost::asio::io_service& GetService () { return m_Service; }; - i2p::util::MemoryPool& GetFragmentsPool () { return m_FragmentsPool; }; - i2p::util::MemoryPool& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; }; - i2p::util::MemoryPool& GetSentMessagesPool () { return m_SentMessagesPool; }; + ~SSUServer(); - uint16_t GetPort () const { return m_Endpoint.port (); }; - bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; - void SetLocalAddress (const boost::asio::ip::address& localAddress); + void Start(); - void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); - void AddRelay (uint32_t tag, std::shared_ptr relay); - void RemoveRelay (uint32_t tag); - std::shared_ptr FindRelaySession (uint32_t tag); - void RescheduleIntroducersUpdateTimer (); - void RescheduleIntroducersUpdateTimerV6 (); + void Stop(); - void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); - PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); - std::shared_ptr GetPeerTestSession (uint32_t nonce); - void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role); - void RemovePeerTest (uint32_t nonce); + bool CreateSession(std::shared_ptr router, bool peerTest = false, + bool v4only = false); - private: + bool CreateSession(std::shared_ptr router, + std::shared_ptr address, bool peerTest = false); - 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 packets, - std::map >* sessions); + void CreateDirectSession(std::shared_ptr router, + boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest); - void CreateSessionThroughIntroducer (std::shared_ptr router, - std::shared_ptr address, bool peerTest = false); - template - std::shared_ptr GetRandomV4Session (Filter filter); - template - std::shared_ptr GetRandomV6Session (Filter filter); + std::shared_ptr FindSession(const boost::asio::ip::udp::endpoint &e) const; - std::list > FindIntroducers (int maxNumIntroducers, bool v4, std::set& excluded); - void ScheduleIntroducersUpdateTimer (); - void ScheduleIntroducersUpdateTimerV6 (); - void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); + std::shared_ptr GetRandomEstablishedV4Session(std::shared_ptr excluded); - void SchedulePeerTestsCleanupTimer (); - void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode); + std::shared_ptr GetRandomEstablishedV6Session(std::shared_ptr excluded); - // timer - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); - void ScheduleTerminationV6 (); - void HandleTerminationTimerV6 (const boost::system::error_code& ecode); + void DeleteSession(std::shared_ptr session); - private: + void DeleteAllSessions(); - struct PeerTest - { - uint64_t creationTime; - PeerTestParticipant role; - std::shared_ptr session; // for Bob to Alice - }; + boost::asio::io_service &GetService() { return m_Service; }; - 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 m_Introducers, m_IntroducersV6; // introducers we are connected to - std::map > m_Sessions, m_SessionsV6; - std::map > m_Relays; // we are introducer - std::map m_PeerTests; // nonce -> creation time in milliseconds + i2p::util::MemoryPool &GetFragmentsPool() { return m_FragmentsPool; }; - i2p::util::MemoryPool m_FragmentsPool; - i2p::util::MemoryPool m_IncompleteMessagesPool; - i2p::util::MemoryPool m_SentMessagesPool; - i2p::util::MemoryPoolMt m_PacketsPool; + i2p::util::MemoryPool &GetIncompleteMessagesPool() { return m_IncompleteMessagesPool; }; - public: - // for HTTP only - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; }; - }; -} + i2p::util::MemoryPool &GetSentMessagesPool() { return m_SentMessagesPool; }; + + uint16_t GetPort() const { return m_Endpoint.port(); }; + + 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 relay); + + void RemoveRelay(uint32_t tag); + + std::shared_ptr FindRelaySession(uint32_t tag); + + void RescheduleIntroducersUpdateTimer(); + + void RescheduleIntroducersUpdateTimerV6(); + + void NewPeerTest(uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); + + PeerTestParticipant GetPeerTestParticipant(uint32_t nonce); + + std::shared_ptr 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 packets, + std::map > *sessions); + + void CreateSessionThroughIntroducer(std::shared_ptr router, + std::shared_ptr address, + bool peerTest = false); + + template + std::shared_ptr GetRandomV4Session(Filter filter); + + template + std::shared_ptr GetRandomV6Session(Filter filter); + + std::list > + FindIntroducers(int maxNumIntroducers, bool v4, std::set &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 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 m_Introducers, m_IntroducersV6; // introducers we are connected to + std::map > m_Sessions, m_SessionsV6; + std::map > m_Relays; // we are introducer + std::map m_PeerTests; // nonce -> creation time in milliseconds + + i2p::util::MemoryPool m_FragmentsPool; + i2p::util::MemoryPool m_IncompleteMessagesPool; + i2p::util::MemoryPool m_SentMessagesPool; + i2p::util::MemoryPoolMt 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 diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 019fffca..fdacfb11 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -14,1018 +14,866 @@ #include "Config.h" #include "SSU2.h" -namespace i2p -{ -namespace transport -{ - SSU2Server::SSU2Server (): - RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"), - m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()), - m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), - m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()), - m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), - m_IsPublished (true), m_IsSyncClockFromPeers (true) - { - } +namespace i2p { + namespace transport { + SSU2Server::SSU2Server() : + RunnableServiceWithWork("SSU2"), m_ReceiveService("SSU2r"), + m_SocketV4(m_ReceiveService.GetService()), m_SocketV6(m_ReceiveService.GetService()), + m_AddressV4(boost::asio::ip::address_v4()), m_AddressV6(boost::asio::ip::address_v6()), + m_TerminationTimer(GetService()), m_ResendTimer(GetService()), + m_IntroducersUpdateTimer(GetService()), m_IntroducersUpdateTimerV6(GetService()), + m_IsPublished(true), m_IsSyncClockFromPeers(true) { + } - void SSU2Server::Start () - { - if (!IsRunning ()) - { - StartIOService (); - i2p::config::GetOption ("ssu2.published", m_IsPublished); - i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); - bool found = false; - auto& addresses = i2p::context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) - { - if (!address) continue; - if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) - { - auto port = address->port; - if (!port) - { - uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (ssu2Port) port = ssu2Port; - else - { - bool ssu; i2p::config::GetOption("ssu", ssu); - uint16_t p; i2p::config::GetOption ("port", p); - if (p) port = ssu ? (p + 1) : p; - } - } - if (port) - { - if (address->IsV4 ()) - { - found = true; - OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port)); - m_ReceiveService.GetService ().post( - [this]() - { - Receive (m_SocketV4); - }); - ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers - } - if (address->IsV6 ()) - { - found = true; - OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port)); - m_ReceiveService.GetService ().post( - [this]() - { - Receive (m_SocketV6); - }); - ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers - } - } - else - LogPrint (eLogError, "SSU2: Can't start server because port not specified"); - } - } - if (found) - m_ReceiveService.Start (); - ScheduleTermination (); - } - } + void SSU2Server::Start() { + if (!IsRunning()) { + StartIOService(); + i2p::config::GetOption("ssu2.published", m_IsPublished); + i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); + bool found = false; + auto &addresses = i2p::context.GetRouterInfo().GetAddresses(); + for (const auto &address: addresses) { + if (!address) continue; + if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) { + auto port = address->port; + if (!port) { + uint16_t ssu2Port; + i2p::config::GetOption("ssu2.port", ssu2Port); + if (ssu2Port) port = ssu2Port; + else { + bool ssu; + i2p::config::GetOption("ssu", ssu); + uint16_t p; + i2p::config::GetOption("port", p); + if (p) port = ssu ? (p + 1) : p; + } + } + if (port) { + if (address->IsV4()) { + found = true; + OpenSocket(boost::asio::ip::udp::endpoint(m_AddressV4, port)); + m_ReceiveService.GetService().post( + [this]() { + Receive(m_SocketV4); + }); + ScheduleIntroducersUpdateTimer(); // wait for 30 seconds and decide if we need introducers + } + if (address->IsV6()) { + found = true; + OpenSocket(boost::asio::ip::udp::endpoint(m_AddressV6, port)); + m_ReceiveService.GetService().post( + [this]() { + Receive(m_SocketV6); + }); + ScheduleIntroducersUpdateTimerV6(); // wait for 30 seconds and decide if we need introducers + } + } else + LogPrint(eLogError, "SSU2: Can't start server because port not specified"); + } + } + if (found) + m_ReceiveService.Start(); + ScheduleTermination(); + } + } - void SSU2Server::Stop () - { - if (IsRunning ()) - { - m_TerminationTimer.cancel (); - m_ResendTimer.cancel (); - m_IntroducersUpdateTimer.cancel (); - m_IntroducersUpdateTimerV6.cancel (); - } - - auto sessions = m_Sessions; - for (auto& it: sessions) - { - it.second->RequestTermination (eSSU2TerminationReasonRouterShutdown); - it.second->Done (); - } - - if (context.SupportsV4 () || context.SupportsV6 ()) - m_ReceiveService.Stop (); - m_SocketV4.close (); - m_SocketV6.close (); - - StopIOService (); + void SSU2Server::Stop() { + if (IsRunning()) { + m_TerminationTimer.cancel(); + m_ResendTimer.cancel(); + m_IntroducersUpdateTimer.cancel(); + m_IntroducersUpdateTimerV6.cancel(); + } - m_Sessions.clear (); - m_SessionsByRouterHash.clear (); - m_PendingOutgoingSessions.clear (); - m_Relays.clear (); - m_Introducers.clear (); - m_IntroducersV6.clear (); - } + auto sessions = m_Sessions; + for (auto &it: sessions) { + it.second->RequestTermination(eSSU2TerminationReasonRouterShutdown); + it.second->Done(); + } - void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) - { - if (localAddress.is_unspecified ()) return; - if (localAddress.is_v4 ()) - { - m_AddressV4 = localAddress; - int mtu = i2p::util::net::GetMTU (localAddress); - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; - i2p::context.SetMTU (mtu, true); - } - else if (localAddress.is_v6 ()) - { - m_AddressV6 = localAddress; - int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ()); - int mtu = i2p::util::net::GetMTU (localAddress); - if (mtu > maxMTU) mtu = maxMTU; - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - i2p::context.SetMTU (mtu, false); - } - } + if (context.SupportsV4() || context.SupportsV6()) + m_ReceiveService.Stop(); + m_SocketV4.close(); + m_SocketV6.close(); - bool SSU2Server::IsSupported (const boost::asio::ip::address& addr) const - { - if (addr.is_v4 ()) - { - if (m_SocketV4.is_open ()) - return true; - } - else if (addr.is_v6 ()) - { - if (m_SocketV6.is_open ()) - return true; - } - return false; - } + StopIOService(); - uint16_t SSU2Server::GetPort (bool v4) const - { - boost::system::error_code ec; - boost::asio::ip::udp::endpoint ep = v4 ? m_SocketV4.local_endpoint (ec) : m_SocketV6.local_endpoint (ec); - if (ec) return 0; - return ep.port (); - } - - boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint) - { - boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4; - try - { - socket.open (localEndpoint.protocol ()); - if (localEndpoint.address ().is_v6 ()) - socket.set_option (boost::asio::ip::v6_only (true)); - socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); - socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE)); - socket.bind (localEndpoint); - LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint); - } - catch (std::exception& ex ) - { - LogPrint (eLogError, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what()); - ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint, ": ", ex.what ()); - } - return socket; - } + m_Sessions.clear(); + m_SessionsByRouterHash.clear(); + m_PendingOutgoingSessions.clear(); + m_Relays.clear(); + m_Introducers.clear(); + m_IntroducersV6.clear(); + } - void SSU2Server::Receive (boost::asio::ip::udp::socket& socket) - { - Packet * packet = m_PacketsPool.AcquireMt (); - socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, - std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket))); - } + void SSU2Server::SetLocalAddress(const boost::asio::ip::address &localAddress) { + if (localAddress.is_unspecified()) return; + if (localAddress.is_v4()) { + m_AddressV4 = localAddress; + int mtu = i2p::util::net::GetMTU(localAddress); + if (mtu < (int) SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; + if (mtu > (int) SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; + i2p::context.SetMTU(mtu, true); + } else if (localAddress.is_v6()) { + m_AddressV6 = localAddress; + int maxMTU = i2p::util::net::GetMaxMTU(localAddress.to_v6()); + int mtu = i2p::util::net::GetMTU(localAddress); + if (mtu > maxMTU) mtu = maxMTU; + if (mtu < (int) SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; + i2p::context.SetMTU(mtu, false); + } + } - void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, - Packet * packet, boost::asio::ip::udp::socket& socket) - { - if (!ecode) - { - i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); - packet->len = bytes_transferred; + bool SSU2Server::IsSupported(const boost::asio::ip::address &addr) const { + if (addr.is_v4()) { + if (m_SocketV4.is_open()) + return true; + } else if (addr.is_v6()) { + if (m_SocketV6.is_open()) + return true; + } + return false; + } - boost::system::error_code ec; - size_t moreBytes = socket.available (ec); - if (!ec && moreBytes) - { - std::vector packets; - packets.push_back (packet); - while (moreBytes && packets.size () < 32) - { - packet = m_PacketsPool.AcquireMt (); - packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); - if (!ec) - { - i2p::transport::transports.UpdateReceivedBytes (packet->len); - packets.push_back (packet); - moreBytes = socket.available(ec); - if (ec) break; - } - else - { - LogPrint (eLogError, "SSU2: receive_from error: code ", ec.value(), ": ", ec.message ()); - m_PacketsPool.ReleaseMt (packet); - break; - } - } - GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets)); - } - else - GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet)); - Receive (socket); - } - else - { - m_PacketsPool.ReleaseMt (packet); - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ()); - auto ep = socket.local_endpoint (); - socket.close (); - OpenSocket (ep); - Receive (socket); - } - } - } + uint16_t SSU2Server::GetPort(bool v4) const { + boost::system::error_code ec; + boost::asio::ip::udp::endpoint ep = v4 ? m_SocketV4.local_endpoint(ec) : m_SocketV6.local_endpoint(ec); + if (ec) return 0; + return ep.port(); + } - void SSU2Server::HandleReceivedPacket (Packet * packet) - { - if (packet) - { - ProcessNextPacket (packet->buf, packet->len, packet->from); - m_PacketsPool.ReleaseMt (packet); - if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) - m_LastSession->FlushData (); - } - } + boost::asio::ip::udp::socket &SSU2Server::OpenSocket(const boost::asio::ip::udp::endpoint &localEndpoint) { + boost::asio::ip::udp::socket &socket = localEndpoint.address().is_v6() ? m_SocketV6 : m_SocketV4; + try { + socket.open(localEndpoint.protocol()); + if (localEndpoint.address().is_v6()) + socket.set_option(boost::asio::ip::v6_only(true)); + socket.set_option(boost::asio::socket_base::receive_buffer_size(SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); + socket.set_option(boost::asio::socket_base::send_buffer_size(SSU2_SOCKET_SEND_BUFFER_SIZE)); + socket.bind(localEndpoint); + LogPrint(eLogInfo, "SSU2: Start listening on ", localEndpoint); + } + catch (std::exception &ex) { + LogPrint(eLogError, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what()); + ThrowFatal("Unable to start SSU2 transport on ", localEndpoint, ": ", ex.what()); + } + return socket; + } - void SSU2Server::HandleReceivedPackets (std::vector packets) - { - for (auto& packet: packets) - ProcessNextPacket (packet->buf, packet->len, packet->from); - m_PacketsPool.ReleaseMt (packets); - if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) - m_LastSession->FlushData (); - } + void SSU2Server::Receive(boost::asio::ip::udp::socket &socket) { + Packet *packet = m_PacketsPool.AcquireMt(); + socket.async_receive_from(boost::asio::buffer(packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, + std::bind(&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, + std::placeholders::_2, packet, std::ref(socket))); + } - void SSU2Server::AddSession (std::shared_ptr session) - { - if (session) - { - m_Sessions.emplace (session->GetConnID (), session); - AddSessionByRouterHash (session); - } - } + void SSU2Server::HandleReceivedFrom(const boost::system::error_code &ecode, size_t bytes_transferred, + Packet *packet, boost::asio::ip::udp::socket &socket) { + if (!ecode) { + i2p::transport::transports.UpdateReceivedBytes(bytes_transferred); + packet->len = bytes_transferred; - void SSU2Server::RemoveSession (uint64_t connID) - { - auto it = m_Sessions.find (connID); - if (it != m_Sessions.end ()) - { - auto ident = it->second->GetRemoteIdentity (); - if (ident) - m_SessionsByRouterHash.erase (ident->GetIdentHash ()); - if (m_LastSession == it->second) - m_LastSession = nullptr; - m_Sessions.erase (it); - } - } - - void SSU2Server::AddSessionByRouterHash (std::shared_ptr session) - { - if (session) - { - auto ident = session->GetRemoteIdentity (); - if (ident) - { - auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session); - if (!ret.second) - { - // session already exists - LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists"); - // terminate existing - GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession)); - // update session - ret.first->second = session; - } - } - } - } + boost::system::error_code ec; + size_t moreBytes = socket.available(ec); + if (!ec && moreBytes) { + std::vector packets; + packets.push_back(packet); + while (moreBytes && packets.size() < 32) { + packet = m_PacketsPool.AcquireMt(); + packet->len = socket.receive_from(boost::asio::buffer(packet->buf, SSU2_MAX_PACKET_SIZE), + packet->from, 0, ec); + if (!ec) { + i2p::transport::transports.UpdateReceivedBytes(packet->len); + packets.push_back(packet); + moreBytes = socket.available(ec); + if (ec) break; + } else { + LogPrint(eLogError, "SSU2: receive_from error: code ", ec.value(), ": ", ec.message()); + m_PacketsPool.ReleaseMt(packet); + break; + } + } + GetService().post(std::bind(&SSU2Server::HandleReceivedPackets, this, packets)); + } else + GetService().post(std::bind(&SSU2Server::HandleReceivedPacket, this, packet)); + Receive(socket); + } else { + m_PacketsPool.ReleaseMt(packet); + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message()); + auto ep = socket.local_endpoint(); + socket.close(); + OpenSocket(ep); + Receive(socket); + } + } + } - bool SSU2Server::AddPendingOutgoingSession (std::shared_ptr session) - { - if (!session) return false; - return m_PendingOutgoingSessions.emplace (session->GetRemoteEndpoint (), session).second; - } + void SSU2Server::HandleReceivedPacket(Packet *packet) { + if (packet) { + ProcessNextPacket(packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt(packet); + if (m_LastSession && m_LastSession->GetState() != eSSU2SessionStateTerminated) + m_LastSession->FlushData(); + } + } - std::shared_ptr SSU2Server::FindSession (const i2p::data::IdentHash& ident) const - { - auto it = m_SessionsByRouterHash.find (ident); - if (it != m_SessionsByRouterHash.end ()) - return it->second; - return nullptr; - } + void SSU2Server::HandleReceivedPackets(std::vector packets) { + for (auto &packet: packets) + ProcessNextPacket(packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt(packets); + if (m_LastSession && m_LastSession->GetState() != eSSU2SessionStateTerminated) + m_LastSession->FlushData(); + } - std::shared_ptr SSU2Server::FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const - { - auto it = m_PendingOutgoingSessions.find (ep); - if (it != m_PendingOutgoingSessions.end ()) - return it->second; - return nullptr; - } + void SSU2Server::AddSession(std::shared_ptr session) { + if (session) { + m_Sessions.emplace(session->GetConnID(), session); + AddSessionByRouterHash(session); + } + } - void SSU2Server::RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) - { - m_PendingOutgoingSessions.erase (ep); - } - - std::shared_ptr SSU2Server::GetRandomSession ( - i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const - { - if (m_Sessions.empty ()) return nullptr; - uint16_t ind; - RAND_bytes ((uint8_t *)&ind, sizeof (ind)); - ind %= m_Sessions.size (); - auto it = m_Sessions.begin (); - std::advance (it, ind); - while (it != m_Sessions.end ()) - { - if ((it->second->GetRemoteTransports () & remoteTransports) && - it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) - return it->second; - it++; - } - // not found, try from begining - it = m_Sessions.begin (); - while (it != m_Sessions.end () && ind) - { - if ((it->second->GetRemoteTransports () & remoteTransports) && - it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) - return it->second; - it++; ind--; - } - return nullptr; - } - - void SSU2Server::AddRelay (uint32_t tag, std::shared_ptr relay) - { - m_Relays.emplace (tag, relay); - } + void SSU2Server::RemoveSession(uint64_t connID) { + auto it = m_Sessions.find(connID); + if (it != m_Sessions.end()) { + auto ident = it->second->GetRemoteIdentity(); + if (ident) + m_SessionsByRouterHash.erase(ident->GetIdentHash()); + if (m_LastSession == it->second) + m_LastSession = nullptr; + m_Sessions.erase(it); + } + } - void SSU2Server::RemoveRelay (uint32_t tag) - { - m_Relays.erase (tag); - } + void SSU2Server::AddSessionByRouterHash(std::shared_ptr session) { + if (session) { + auto ident = session->GetRemoteIdentity(); + if (ident) { + auto ret = m_SessionsByRouterHash.emplace(ident->GetIdentHash(), session); + if (!ret.second) { + // session already exists + LogPrint(eLogWarning, "SSU2: Session to ", ident->GetIdentHash().ToBase64(), " already exists"); + // terminate existing + GetService().post(std::bind(&SSU2Session::RequestTermination, ret.first->second, + eSSU2TerminationReasonReplacedByNewSession)); + // update session + ret.first->second = session; + } + } + } + } - std::shared_ptr SSU2Server::FindRelaySession (uint32_t tag) - { - auto it = m_Relays.find (tag); - if (it != m_Relays.end ()) - { - if (it->second->IsEstablished ()) - return it->second; - else - m_Relays.erase (it); - } - return nullptr; - } + bool SSU2Server::AddPendingOutgoingSession(std::shared_ptr session) { + if (!session) return false; + return m_PendingOutgoingSessions.emplace(session->GetRemoteEndpoint(), session).second; + } - void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - if (len < 24) return; - uint64_t connID; - memcpy (&connID, buf, 8); - connID ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - if (!m_LastSession || m_LastSession->GetConnID () != connID) - { - if (m_LastSession) m_LastSession->FlushData (); - auto it = m_Sessions.find (connID); - if (it != m_Sessions.end ()) - m_LastSession = it->second; - else - m_LastSession = nullptr; - } - if (m_LastSession) - { - switch (m_LastSession->GetState ()) - { - case eSSU2SessionStateEstablished: - case eSSU2SessionStateSessionConfirmedSent: - m_LastSession->ProcessData (buf, len); - break; - case eSSU2SessionStateSessionCreatedSent: - if (!m_LastSession->ProcessSessionConfirmed (buf, len)) - { - m_LastSession->Done (); - m_LastSession = nullptr; - } - break; - case eSSU2SessionStateIntroduced: - if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ()) - m_LastSession->SetRemoteEndpoint (senderEndpoint); - if (m_LastSession->GetRemoteEndpoint () == senderEndpoint) - m_LastSession->ProcessHolePunch (buf, len); - else - { - LogPrint (eLogWarning, "SSU2: HolePunch endpoint ", senderEndpoint, - " doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ()); - m_LastSession->Done (); - m_LastSession = nullptr; - } - break; - case eSSU2SessionStatePeerTest: - m_LastSession->SetRemoteEndpoint (senderEndpoint); - m_LastSession->ProcessPeerTest (buf, len); - break; - case eSSU2SessionStateClosing: - m_LastSession->ProcessData (buf, len); // we might receive termintaion block - if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) - m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again - break; - case eSSU2SessionStateTerminated: - m_LastSession = nullptr; - break; - default: - LogPrint (eLogWarning, "SSU2: Invalid session state ", (int)m_LastSession->GetState ()); - } - } - else - { - // check pending sessions if it's SessionCreated or Retry - auto it1 = m_PendingOutgoingSessions.find (senderEndpoint); - if (it1 != m_PendingOutgoingSessions.end ()) - { - if (it1->second->GetState () == eSSU2SessionStateSessionRequestSent && - it1->second->ProcessSessionCreated (buf, len)) - m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint - else - it1->second->ProcessRetry (buf, len); - } - else - { - // assume new incoming session - auto session = std::make_shared (*this); - session->SetRemoteEndpoint (senderEndpoint); - session->ProcessFirstIncomingMessage (connID, buf, len); - } - } - } + std::shared_ptr SSU2Server::FindSession(const i2p::data::IdentHash &ident) const { + auto it = m_SessionsByRouterHash.find(ident); + if (it != m_SessionsByRouterHash.end()) + return it->second; + return nullptr; + } - void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, - const boost::asio::ip::udp::endpoint& to) - { - std::vector bufs - { - boost::asio::buffer (header, headerLen), - boost::asio::buffer (payload, payloadLen) - }; - boost::system::error_code ec; - if (to.address ().is_v6 ()) - m_SocketV6.send_to (bufs, to, 0, ec); - else - m_SocketV4.send_to (bufs, to, 0, ec); - if (!ec) - i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); - else - LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); - } + std::shared_ptr + SSU2Server::FindPendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep) const { + auto it = m_PendingOutgoingSessions.find(ep); + if (it != m_PendingOutgoingSessions.end()) + return it->second; + return nullptr; + } - void SSU2Server::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) - { - std::vector bufs - { - boost::asio::buffer (header, headerLen), - boost::asio::buffer (headerX, headerXLen), - boost::asio::buffer (payload, payloadLen) - }; - boost::system::error_code ec; - if (to.address ().is_v6 ()) - m_SocketV6.send_to (bufs, to, 0, ec); - else - m_SocketV4.send_to (bufs, to, 0, ec); + void SSU2Server::RemovePendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep) { + m_PendingOutgoingSessions.erase(ep); + } - if (!ec) - i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen); - else - LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); - } + std::shared_ptr SSU2Server::GetRandomSession( + i2p::data::RouterInfo::CompatibleTransports remoteTransports, + const i2p::data::IdentHash &excluded) const { + if (m_Sessions.empty()) return nullptr; + uint16_t ind; + RAND_bytes((uint8_t * ) & ind, sizeof(ind)); + ind %= m_Sessions.size(); + auto it = m_Sessions.begin(); + std::advance(it, ind); + while (it != m_Sessions.end()) { + if ((it->second->GetRemoteTransports() & remoteTransports) && + it->second->GetRemoteIdentity()->GetIdentHash() != excluded) + return it->second; + it++; + } + // not found, try from begining + it = m_Sessions.begin(); + while (it != m_Sessions.end() && ind) { + if ((it->second->GetRemoteTransports() & remoteTransports) && + it->second->GetRemoteIdentity()->GetIdentHash() != excluded) + return it->second; + it++; + ind--; + } + return nullptr; + } - bool SSU2Server::CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest) - { - if (router && address) - { - // check if no session - auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); - if (it != m_SessionsByRouterHash.end ()) - { - // session with router found, trying to send peer test if requested - if (peerTest && it->second->IsEstablished ()) - { - auto session = it->second; - GetService ().post ([session]() { session->SendPeerTest (); }); - } - return false; - } - // check is no pending session - bool isValidEndpoint = !address->host.is_unspecified () && address->port; - if (isValidEndpoint) - { - if (i2p::util::net::IsInReservedRange(address->host)) return false; - auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); - if (s) - { - if (peerTest) - { - // if peer test requested add it to the list for pending session - auto onEstablished = s->GetOnEstablished (); - if (onEstablished) - s->SetOnEstablished ([s, onEstablished]() - { - onEstablished (); - s->SendPeerTest (); - }); - else - s->SetOnEstablished ([s]() { s->SendPeerTest (); }); - } - return false; - } - } - - auto session = std::make_shared (*this, router, address); - if (peerTest) - session->SetOnEstablished ([session]() {session->SendPeerTest (); }); + void SSU2Server::AddRelay(uint32_t tag, std::shared_ptr relay) { + m_Relays.emplace(tag, relay); + } - if (address->UsesIntroducer ()) - GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); - else if (isValidEndpoint) // we can't connect without endpoint - GetService ().post ([session]() { session->Connect (); }); - else - return false; - } - else - return false; - return true; - } + void SSU2Server::RemoveRelay(uint32_t tag) { + m_Relays.erase(tag); + } - void SSU2Server::ConnectThroughIntroducer (std::shared_ptr session) - { - if (!session) return; - auto address = session->GetAddress (); - if (!address) return; - session->WaitForIntroduction (); - // try to find existing session first - for (auto& it: address->ssu->introducers) - { - auto it1 = m_SessionsByRouterHash.find (it.iKey); - if (it1 != m_SessionsByRouterHash.end ()) - { - it1->second->Introduce (session, it.iTag); - return; - } - } - // we have to start a new session to an introducer - auto ts = i2p::util::GetSecondsSinceEpoch (); - std::shared_ptr r; - uint32_t relayTag = 0; - if (!address->ssu->introducers.empty ()) - { - std::vector indicies; - for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indicies.push_back(i); - if (indicies.size () > 1) - std::shuffle (indicies.begin(), indicies.end(), std::mt19937(std::random_device()())); + std::shared_ptr SSU2Server::FindRelaySession(uint32_t tag) { + auto it = m_Relays.find(tag); + if (it != m_Relays.end()) { + if (it->second->IsEstablished()) + return it->second; + else + m_Relays.erase(it); + } + return nullptr; + } - for (auto i: indicies) - { - const auto& introducer = address->ssu->introducers[indicies[i]]; - if (introducer.iTag && ts < introducer.iExp) - { - r = i2p::data::netdb.FindRouter (introducer.iKey); - if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ())) - { - relayTag = introducer.iTag; - if (relayTag) break; - } - } - } - } - if (r) - { - if (relayTag) - { - // introducer and tag found connect to it through SSU2 - auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); - if (addr) - { - bool isValidEndpoint = !addr->host.is_unspecified () && addr->port && - !i2p::util::net::IsInReservedRange(addr->host); - if (isValidEndpoint) - { - auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port)); - if (!s) - { - s = std::make_shared (*this, r, addr); - s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); }); - s->Connect (); - } - else - { - auto onEstablished = s->GetOnEstablished (); - if (onEstablished) - s->SetOnEstablished ([session, s, relayTag, onEstablished]() - { - onEstablished (); - s->Introduce (session, relayTag); - }); - else - s->SetOnEstablished ([session, s, relayTag]() {s->Introduce (session, relayTag); }); - } - } - } - } - } - else - { - // introducers not found, try to request them - for (auto& it: address->ssu->introducers) - if (it.iTag && ts < it.iExp) - i2p::data::netdb.RequestDestination (it.iKey); - } - } + void + SSU2Server::ProcessNextPacket(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint) { + if (len < 24) return; + uint64_t connID; + memcpy(&connID, buf, 8); + connID ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 24)); + if (!m_LastSession || m_LastSession->GetConnID() != connID) { + if (m_LastSession) m_LastSession->FlushData(); + auto it = m_Sessions.find(connID); + if (it != m_Sessions.end()) + m_LastSession = it->second; + else + m_LastSession = nullptr; + } + if (m_LastSession) { + switch (m_LastSession->GetState()) { + case eSSU2SessionStateEstablished: + case eSSU2SessionStateSessionConfirmedSent: + m_LastSession->ProcessData(buf, len); + break; + case eSSU2SessionStateSessionCreatedSent: + if (!m_LastSession->ProcessSessionConfirmed(buf, len)) { + m_LastSession->Done(); + m_LastSession = nullptr; + } + break; + case eSSU2SessionStateIntroduced: + if (m_LastSession->GetRemoteEndpoint().address().is_unspecified()) + m_LastSession->SetRemoteEndpoint(senderEndpoint); + if (m_LastSession->GetRemoteEndpoint() == senderEndpoint) + m_LastSession->ProcessHolePunch(buf, len); + else { + LogPrint(eLogWarning, "SSU2: HolePunch endpoint ", senderEndpoint, + " doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint()); + m_LastSession->Done(); + m_LastSession = nullptr; + } + break; + case eSSU2SessionStatePeerTest: + m_LastSession->SetRemoteEndpoint(senderEndpoint); + m_LastSession->ProcessPeerTest(buf, len); + break; + case eSSU2SessionStateClosing: + m_LastSession->ProcessData(buf, len); // we might receive termintaion block + if (m_LastSession && m_LastSession->GetState() != eSSU2SessionStateTerminated) + m_LastSession->RequestTermination( + eSSU2TerminationReasonIdleTimeout); // send termination again + break; + case eSSU2SessionStateTerminated: + m_LastSession = nullptr; + break; + default: + LogPrint(eLogWarning, "SSU2: Invalid session state ", (int) m_LastSession->GetState()); + } + } else { + // check pending sessions if it's SessionCreated or Retry + auto it1 = m_PendingOutgoingSessions.find(senderEndpoint); + if (it1 != m_PendingOutgoingSessions.end()) { + if (it1->second->GetState() == eSSU2SessionStateSessionRequestSent && + it1->second->ProcessSessionCreated(buf, len)) + m_PendingOutgoingSessions.erase(it1); // we are done with that endpoint + else + it1->second->ProcessRetry(buf, len); + } else { + // assume new incoming session + auto session = std::make_shared(*this); + session->SetRemoteEndpoint(senderEndpoint); + session->ProcessFirstIncomingMessage(connID, buf, len); + } + } + } - bool SSU2Server::StartPeerTest (std::shared_ptr router, bool v4) - { - if (!router) return false; - auto addr = v4 ? router->GetSSU2V4Address () : router->GetSSU2V6Address (); - if (!addr) return false; - auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); - if (it != m_SessionsByRouterHash.end ()) - { - auto s = it->second; - if (it->second->IsEstablished ()) - GetService ().post ([s]() { s->SendPeerTest (); }); - else - s->SetOnEstablished ([s]() { s->SendPeerTest (); }); - return true; - } - else - CreateSession (router, addr, true); - return true; - } - - void SSU2Server::ScheduleTermination () - { - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU2_TERMINATION_CHECK_TIMEOUT)); - m_TerminationTimer.async_wait (std::bind (&SSU2Server::HandleTerminationTimer, - this, std::placeholders::_1)); - } + void SSU2Server::Send(const uint8_t *header, size_t headerLen, const uint8_t *payload, size_t payloadLen, + const boost::asio::ip::udp::endpoint &to) { + std::vector bufs + { + boost::asio::buffer(header, headerLen), + boost::asio::buffer(payload, payloadLen) + }; + boost::system::error_code ec; + if (to.address().is_v6()) + m_SocketV6.send_to(bufs, to, 0, ec); + else + m_SocketV4.send_to(bufs, to, 0, ec); + if (!ec) + i2p::transport::transports.UpdateSentBytes(headerLen + payloadLen); + else + LogPrint(eLogError, "SSU2: Send exception: ", ec.message(), " to ", to); + } - void SSU2Server::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_PendingOutgoingSessions.begin (); it != m_PendingOutgoingSessions.end ();) - { - if (it->second->IsTerminationTimeoutExpired (ts)) - { - //it->second->Terminate (); - it = m_PendingOutgoingSessions.erase (it); - } - else - it++; - } + void SSU2Server::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) { + std::vector bufs + { + boost::asio::buffer(header, headerLen), + boost::asio::buffer(headerX, headerXLen), + boost::asio::buffer(payload, payloadLen) + }; + boost::system::error_code ec; + if (to.address().is_v6()) + m_SocketV6.send_to(bufs, to, 0, ec); + else + m_SocketV4.send_to(bufs, to, 0, ec); - for (auto it: m_Sessions) - { - auto state = it.second->GetState (); - if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing) - it.second->Done (); - else if (it.second->IsTerminationTimeoutExpired (ts)) - { - if (it.second->IsEstablished ()) - it.second->RequestTermination (eSSU2TerminationReasonIdleTimeout); - else - it.second->Done (); - } - else - it.second->CleanUp (ts); - } + if (!ec) + i2p::transport::transports.UpdateSentBytes(headerLen + headerXLen + payloadLen); + else + LogPrint(eLogError, "SSU2: Send exception: ", ec.message(), " to ", to); + } - for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();) - { - if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) - it = m_SessionsByRouterHash.erase (it); - else - it++; - } + bool SSU2Server::CreateSession(std::shared_ptr router, + std::shared_ptr address, bool peerTest) { + if (router && address) { + // check if no session + auto it = m_SessionsByRouterHash.find(router->GetIdentHash()); + if (it != m_SessionsByRouterHash.end()) { + // session with router found, trying to send peer test if requested + if (peerTest && it->second->IsEstablished()) { + auto session = it->second; + GetService().post([session]() { session->SendPeerTest(); }); + } + return false; + } + // check is no pending session + bool isValidEndpoint = !address->host.is_unspecified() && address->port; + if (isValidEndpoint) { + if (i2p::util::net::IsInReservedRange(address->host)) return false; + auto s = FindPendingOutgoingSession(boost::asio::ip::udp::endpoint(address->host, address->port)); + if (s) { + if (peerTest) { + // if peer test requested add it to the list for pending session + auto onEstablished = s->GetOnEstablished(); + if (onEstablished) + s->SetOnEstablished([s, onEstablished]() { + onEstablished(); + s->SendPeerTest(); + }); + else + s->SetOnEstablished([s]() { s->SendPeerTest(); }); + } + return false; + } + } - for (auto it = m_Relays.begin (); it != m_Relays.begin ();) - { - if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) - it = m_Relays.erase (it); - else - it++; - } - - for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); ) - { - if (ts > it->second.second) - it = m_IncomingTokens.erase (it); - else - it++; - } + auto session = std::make_shared(*this, router, address); + if (peerTest) + session->SetOnEstablished([session]() { session->SendPeerTest(); }); - for (auto it = m_OutgoingTokens.begin (); it != m_OutgoingTokens.end (); ) - { - if (ts > it->second.second) - it = m_OutgoingTokens.erase (it); - else - it++; - } - - m_PacketsPool.CleanUpMt (); - m_SentPacketsPool.CleanUp (); - ScheduleTermination (); - } - } + if (address->UsesIntroducer()) + GetService().post(std::bind(&SSU2Server::ConnectThroughIntroducer, this, session)); + else if (isValidEndpoint) // we can't connect without endpoint + GetService().post([session]() { session->Connect(); }); + else + return false; + } else + return false; + return true; + } - void SSU2Server::ScheduleResend () - { - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(SSU2_RESEND_CHECK_TIMEOUT)); - m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer, - this, std::placeholders::_1)); - } + void SSU2Server::ConnectThroughIntroducer(std::shared_ptr session) { + if (!session) return; + auto address = session->GetAddress(); + if (!address) return; + session->WaitForIntroduction(); + // try to find existing session first + for (auto &it: address->ssu->introducers) { + auto it1 = m_SessionsByRouterHash.find(it.iKey); + if (it1 != m_SessionsByRouterHash.end()) { + it1->second->Introduce(session, it.iTag); + return; + } + } + // we have to start a new session to an introducer + auto ts = i2p::util::GetSecondsSinceEpoch(); + std::shared_ptr r; + uint32_t relayTag = 0; + if (!address->ssu->introducers.empty()) { + std::vector indicies; + for (int i = 0; i < (int) address->ssu->introducers.size(); i++) indicies.push_back(i); + if (indicies.size() > 1) + std::shuffle(indicies.begin(), indicies.end(), std::mt19937(std::random_device()())); - void SSU2Server::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it: m_Sessions) - it.second->Resend (ts); - for (auto it: m_PendingOutgoingSessions) - it.second->Resend (ts); - ScheduleResend (); - } - } + for (auto i: indicies) { + const auto &introducer = address->ssu->introducers[indicies[i]]; + if (introducer.iTag && ts < introducer.iExp) { + r = i2p::data::netdb.FindRouter(introducer.iKey); + if (r && r->IsReachableFrom(i2p::context.GetRouterInfo())) { + relayTag = introducer.iTag; + if (relayTag) break; + } + } + } + } + if (r) { + if (relayTag) { + // introducer and tag found connect to it through SSU2 + auto addr = address->IsV6() ? r->GetSSU2V6Address() : r->GetSSU2V4Address(); + if (addr) { + bool isValidEndpoint = !addr->host.is_unspecified() && addr->port && + !i2p::util::net::IsInReservedRange(addr->host); + if (isValidEndpoint) { + auto s = FindPendingOutgoingSession(boost::asio::ip::udp::endpoint(addr->host, addr->port)); + if (!s) { + s = std::make_shared(*this, r, addr); + s->SetOnEstablished([session, s, relayTag]() { s->Introduce(session, relayTag); }); + s->Connect(); + } else { + auto onEstablished = s->GetOnEstablished(); + if (onEstablished) + s->SetOnEstablished([session, s, relayTag, onEstablished]() { + onEstablished(); + s->Introduce(session, relayTag); + }); + else + s->SetOnEstablished([session, s, relayTag]() { s->Introduce(session, relayTag); }); + } + } + } + } + } else { + // introducers not found, try to request them + for (auto &it: address->ssu->introducers) + if (it.iTag && ts < it.iExp) + i2p::data::netdb.RequestDestination(it.iKey); + } + } - void SSU2Server::UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp) - { - m_OutgoingTokens[ep] = {token, exp}; - } + bool SSU2Server::StartPeerTest(std::shared_ptr router, bool v4) { + if (!router) return false; + auto addr = v4 ? router->GetSSU2V4Address() : router->GetSSU2V6Address(); + if (!addr) return false; + auto it = m_SessionsByRouterHash.find(router->GetIdentHash()); + if (it != m_SessionsByRouterHash.end()) { + auto s = it->second; + if (it->second->IsEstablished()) + GetService().post([s]() { s->SendPeerTest(); }); + else + s->SetOnEstablished([s]() { s->SendPeerTest(); }); + return true; + } else + CreateSession(router, addr, true); + return true; + } - uint64_t SSU2Server::FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const - { - auto it = m_OutgoingTokens.find (ep); - if (it != m_OutgoingTokens.end ()) - { - if (i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_THRESHOLD > it->second.second) - return 0; // token expired - return it->second.first; - } - return 0; - } + void SSU2Server::ScheduleTermination() { + m_TerminationTimer.expires_from_now(boost::posix_time::seconds(SSU2_TERMINATION_CHECK_TIMEOUT)); + m_TerminationTimer.async_wait(std::bind(&SSU2Server::HandleTerminationTimer, + this, std::placeholders::_1)); + } - uint64_t SSU2Server::GetIncomingToken (const boost::asio::ip::udp::endpoint& ep) - { - auto it = m_IncomingTokens.find (ep); - if (it != m_IncomingTokens.end ()) - return it->second.first; - uint64_t token; - RAND_bytes ((uint8_t *)&token, 8); - m_IncomingTokens.emplace (ep, std::make_pair (token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT)); - return token; - } + void SSU2Server::HandleTerminationTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ts = i2p::util::GetSecondsSinceEpoch(); + for (auto it = m_PendingOutgoingSessions.begin(); it != m_PendingOutgoingSessions.end();) { + if (it->second->IsTerminationTimeoutExpired(ts)) { + //it->second->Terminate (); + it = m_PendingOutgoingSessions.erase(it); + } else + it++; + } - std::pair SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep) - { - m_IncomingTokens.erase (ep); // drop previous - uint64_t token; - RAND_bytes ((uint8_t *)&token, 8); - auto ret = std::make_pair (token, i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT); - m_IncomingTokens.emplace (ep, ret); - return ret; - } + for (auto it: m_Sessions) { + auto state = it.second->GetState(); + if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing) + it.second->Done(); + else if (it.second->IsTerminationTimeoutExpired(ts)) { + if (it.second->IsEstablished()) + it.second->RequestTermination(eSSU2TerminationReasonIdleTimeout); + else + it.second->Done(); + } else + it.second->CleanUp(ts); + } - std::list > SSU2Server::FindIntroducers (int maxNumIntroducers, - bool v4, const std::set& excluded) const - { - std::list > ret; - for (const auto& s : m_Sessions) - { - if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) && - !excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) && - ((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) || - (!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6)))) - ret.push_back (s.second); - } - if ((int)ret.size () > maxNumIntroducers) - { - // shink ret randomly - int sz = ret.size () - maxNumIntroducers; - for (int i = 0; i < sz; i++) - { - auto ind = rand () % ret.size (); - auto it = ret.begin (); - std::advance (it, ind); - ret.erase (it); - } - } - return ret; - } + for (auto it = m_SessionsByRouterHash.begin(); it != m_SessionsByRouterHash.begin();) { + if (it->second && it->second->GetState() == eSSU2SessionStateTerminated) + it = m_SessionsByRouterHash.erase(it); + else + it++; + } - void SSU2Server::UpdateIntroducers (bool v4) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::list newList; - auto& introducers = v4 ? m_Introducers : m_IntroducersV6; - std::set excluded; - for (const auto& it : introducers) - { - std::shared_ptr session; - auto it1 = m_SessionsByRouterHash.find (it); - if (it1 != m_SessionsByRouterHash.end ()) - { - session = it1->second; - excluded.insert (it); - } - if (session && session->IsEstablished ()) - { - if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION) - session->SendKeepAlive (); - if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION) - newList.push_back (it); - else - session = nullptr; - } - if (!session) - i2p::context.RemoveSSU2Introducer (it, v4); - } - if (newList.size () < SSU2_MAX_NUM_INTRODUCERS) - { - auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded); - if (sessions.empty () && !introducers.empty ()) - { - // bump creation time for previous introducers if no new sessions found - LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing"); - for (auto& it : introducers) - { - auto it1 = m_SessionsByRouterHash.find (it); - if (it1 != m_SessionsByRouterHash.end ()) - { - auto session = it1->second; - if (session->IsEstablished ()) - { - session->SetCreationTime (session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION); - if (std::find (newList.begin (), newList.end (), it) == newList.end ()) - { - newList.push_back (it); - sessions.push_back (session); - } - } - } - } - } - - for (const auto& it : sessions) - { - i2p::data::RouterInfo::Introducer introducer; - introducer.iTag = it->GetRelayTag (); - introducer.iKey = it->GetRemoteIdentity ()->GetIdentHash (); - introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; - excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ()); - if (i2p::context.AddSSU2Introducer (introducer, v4)) - { - LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ", - i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ())); - newList.push_back (it->GetRemoteIdentity ()->GetIdentHash ()); - if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break; - } - } - } - introducers = newList; + for (auto it = m_Relays.begin(); it != m_Relays.begin();) { + if (it->second && it->second->GetState() == eSSU2SessionStateTerminated) + it = m_Relays.erase(it); + else + it++; + } - if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS) - { - for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS; i++) - { - auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded); - if (introducer) - { - auto address = v4 ? introducer->GetSSU2V4Address () : introducer->GetSSU2V6Address (); - if (address) - { - CreateSession (introducer, address); - excluded.insert (introducer->GetIdentHash ()); - } - } - else - { - LogPrint (eLogDebug, "SSU2: Can't find more introducers"); - break; - } - } - } - } + for (auto it = m_IncomingTokens.begin(); it != m_IncomingTokens.end();) { + if (ts > it->second.second) + it = m_IncomingTokens.erase(it); + else + it++; + } - void SSU2Server::ScheduleIntroducersUpdateTimer () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } - } + for (auto it = m_OutgoingTokens.begin(); it != m_OutgoingTokens.end();) { + if (ts > it->second.second) + it = m_OutgoingTokens.erase(it); + else + it++; + } - void SSU2Server::RescheduleIntroducersUpdateTimer () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimer.cancel (); - i2p::context.ClearSSU2Introducers (true); - m_Introducers.clear (); - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } - } + m_PacketsPool.CleanUpMt(); + m_SentPacketsPool.CleanUp(); + ScheduleTermination(); + } + } - void SSU2Server::ScheduleIntroducersUpdateTimerV6 () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } - } + void SSU2Server::ScheduleResend() { + m_ResendTimer.expires_from_now(boost::posix_time::milliseconds(SSU2_RESEND_CHECK_TIMEOUT)); + m_ResendTimer.async_wait(std::bind(&SSU2Server::HandleResendTimer, + this, std::placeholders::_1)); + } - void SSU2Server::RescheduleIntroducersUpdateTimerV6 () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimerV6.cancel (); - i2p::context.ClearSSU2Introducers (false); - m_IntroducersV6.clear (); - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } - } - - void SSU2Server::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4) - { - if (ecode != boost::asio::error::operation_aborted) - { - // timeout expired - if (v4) - { - if (i2p::context.GetStatus () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimer (); - return; - } - if (i2p::context.GetStatus () != eRouterStatusFirewalled) - { - // we don't need introducers - i2p::context.ClearSSU2Introducers (true); - m_Introducers.clear (); - return; - } - // we are firewalled - auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address (); - if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachableSSU2 (true, false); // v4 - - UpdateIntroducers (true); - ScheduleIntroducersUpdateTimer (); - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimerV6 (); - return; - } - if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled) - { - // we don't need introducers - i2p::context.ClearSSU2Introducers (false); - m_IntroducersV6.clear (); - return; - } - // we are firewalled - auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address (); - if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachableSSU2 (false, true); // v6 - - UpdateIntroducers (false); - ScheduleIntroducersUpdateTimerV6 (); - } - } - } -} + void SSU2Server::HandleResendTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + for (auto it: m_Sessions) + it.second->Resend(ts); + for (auto it: m_PendingOutgoingSessions) + it.second->Resend(ts); + ScheduleResend(); + } + } + + void SSU2Server::UpdateOutgoingToken(const boost::asio::ip::udp::endpoint &ep, uint64_t token, uint32_t exp) { + m_OutgoingTokens[ep] = {token, exp}; + } + + uint64_t SSU2Server::FindOutgoingToken(const boost::asio::ip::udp::endpoint &ep) const { + auto it = m_OutgoingTokens.find(ep); + if (it != m_OutgoingTokens.end()) { + if (i2p::util::GetSecondsSinceEpoch() + SSU2_TOKEN_EXPIRATION_THRESHOLD > it->second.second) + return 0; // token expired + return it->second.first; + } + return 0; + } + + uint64_t SSU2Server::GetIncomingToken(const boost::asio::ip::udp::endpoint &ep) { + auto it = m_IncomingTokens.find(ep); + if (it != m_IncomingTokens.end()) + return it->second.first; + uint64_t token; + RAND_bytes((uint8_t * ) & token, 8); + m_IncomingTokens.emplace(ep, std::make_pair(token, i2p::util::GetSecondsSinceEpoch() + + SSU2_TOKEN_EXPIRATION_TIMEOUT)); + return token; + } + + std::pair SSU2Server::NewIncomingToken(const boost::asio::ip::udp::endpoint &ep) { + m_IncomingTokens.erase(ep); // drop previous + uint64_t token; + RAND_bytes((uint8_t * ) & token, 8); + auto ret = std::make_pair(token, i2p::util::GetSecondsSinceEpoch() + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT); + m_IncomingTokens.emplace(ep, ret); + return ret; + } + + std::list > SSU2Server::FindIntroducers(int maxNumIntroducers, + bool v4, + const std::set &excluded) const { + std::list > ret; + for (const auto &s: m_Sessions) { + if (s.second->IsEstablished() && (s.second->GetRelayTag() && s.second->IsOutgoing()) && + !excluded.count(s.second->GetRemoteIdentity()->GetIdentHash()) && + ((v4 && (s.second->GetRemoteTransports() & i2p::data::RouterInfo::eSSU2V4)) || + (!v4 && (s.second->GetRemoteTransports() & i2p::data::RouterInfo::eSSU2V6)))) + ret.push_back(s.second); + } + if ((int) ret.size() > maxNumIntroducers) { + // shink ret randomly + int sz = ret.size() - maxNumIntroducers; + for (int i = 0; i < sz; i++) { + auto ind = rand() % ret.size(); + auto it = ret.begin(); + std::advance(it, ind); + ret.erase(it); + } + } + return ret; + } + + void SSU2Server::UpdateIntroducers(bool v4) { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + std::list newList; + auto &introducers = v4 ? m_Introducers : m_IntroducersV6; + std::set excluded; + for (const auto &it: introducers) { + std::shared_ptr session; + auto it1 = m_SessionsByRouterHash.find(it); + if (it1 != m_SessionsByRouterHash.end()) { + session = it1->second; + excluded.insert(it); + } + if (session && session->IsEstablished()) { + if (ts < session->GetCreationTime() + SSU2_TO_INTRODUCER_SESSION_EXPIRATION) + session->SendKeepAlive(); + if (ts < session->GetCreationTime() + SSU2_TO_INTRODUCER_SESSION_DURATION) + newList.push_back(it); + else + session = nullptr; + } + if (!session) + i2p::context.RemoveSSU2Introducer(it, v4); + } + if (newList.size() < SSU2_MAX_NUM_INTRODUCERS) { + auto sessions = FindIntroducers(SSU2_MAX_NUM_INTRODUCERS - newList.size(), v4, excluded); + if (sessions.empty() && !introducers.empty()) { + // bump creation time for previous introducers if no new sessions found + LogPrint(eLogDebug, "SSU2: No new introducers found. Trying to reuse existing"); + for (auto &it: introducers) { + auto it1 = m_SessionsByRouterHash.find(it); + if (it1 != m_SessionsByRouterHash.end()) { + auto session = it1->second; + if (session->IsEstablished()) { + session->SetCreationTime( + session->GetCreationTime() + SSU2_TO_INTRODUCER_SESSION_DURATION); + if (std::find(newList.begin(), newList.end(), it) == newList.end()) { + newList.push_back(it); + sessions.push_back(session); + } + } + } + } + } + + for (const auto &it: sessions) { + i2p::data::RouterInfo::Introducer introducer; + introducer.iTag = it->GetRelayTag(); + introducer.iKey = it->GetRemoteIdentity()->GetIdentHash(); + introducer.iExp = it->GetCreationTime() + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; + excluded.insert(it->GetRemoteIdentity()->GetIdentHash()); + if (i2p::context.AddSSU2Introducer(introducer, v4)) { + LogPrint(eLogDebug, "SSU2: Introducer added ", it->GetRelayTag(), " at ", + i2p::data::GetIdentHashAbbreviation(it->GetRemoteIdentity()->GetIdentHash())); + newList.push_back(it->GetRemoteIdentity()->GetIdentHash()); + if (newList.size() >= SSU2_MAX_NUM_INTRODUCERS) break; + } + } + } + introducers = newList; + + if (introducers.size() < SSU2_MAX_NUM_INTRODUCERS) { + for (auto i = introducers.size(); i < SSU2_MAX_NUM_INTRODUCERS; i++) { + auto introducer = i2p::data::netdb.GetRandomSSU2Introducer(v4, excluded); + if (introducer) { + auto address = v4 ? introducer->GetSSU2V4Address() : introducer->GetSSU2V6Address(); + if (address) { + CreateSession(introducer, address); + excluded.insert(introducer->GetIdentHash()); + } + } else { + LogPrint(eLogDebug, "SSU2: Can't find more introducers"); + break; + } + } + } + } + + void SSU2Server::ScheduleIntroducersUpdateTimer() { + if (m_IsPublished) { + m_IntroducersUpdateTimer.expires_from_now(boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimer.async_wait(std::bind(&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, true)); + } + } + + void SSU2Server::RescheduleIntroducersUpdateTimer() { + if (m_IsPublished) { + m_IntroducersUpdateTimer.cancel(); + i2p::context.ClearSSU2Introducers(true); + m_Introducers.clear(); + m_IntroducersUpdateTimer.expires_from_now(boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL / 2)); + m_IntroducersUpdateTimer.async_wait(std::bind(&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, true)); + } + } + + void SSU2Server::ScheduleIntroducersUpdateTimerV6() { + if (m_IsPublished) { + m_IntroducersUpdateTimerV6.expires_from_now(boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimerV6.async_wait(std::bind(&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, false)); + } + } + + void SSU2Server::RescheduleIntroducersUpdateTimerV6() { + if (m_IsPublished) { + m_IntroducersUpdateTimerV6.cancel(); + i2p::context.ClearSSU2Introducers(false); + m_IntroducersV6.clear(); + m_IntroducersUpdateTimerV6.expires_from_now(boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL / 2)); + m_IntroducersUpdateTimerV6.async_wait(std::bind(&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, false)); + } + } + + void SSU2Server::HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4) { + if (ecode != boost::asio::error::operation_aborted) { + // timeout expired + if (v4) { + if (i2p::context.GetStatus() == eRouterStatusTesting) { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimer(); + return; + } + if (i2p::context.GetStatus() != eRouterStatusFirewalled) { + // we don't need introducers + i2p::context.ClearSSU2Introducers(true); + m_Introducers.clear(); + return; + } + // we are firewalled + auto addr = i2p::context.GetRouterInfo().GetSSU2V4Address(); + if (addr && addr->ssu && addr->ssu->introducers.empty()) + i2p::context.SetUnreachableSSU2(true, false); // v4 + + UpdateIntroducers(true); + ScheduleIntroducersUpdateTimer(); + } else { + if (i2p::context.GetStatusV6() == eRouterStatusTesting) { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimerV6(); + return; + } + if (i2p::context.GetStatusV6() != eRouterStatusFirewalled) { + // we don't need introducers + i2p::context.ClearSSU2Introducers(false); + m_IntroducersV6.clear(); + return; + } + // we are firewalled + auto addr = i2p::context.GetRouterInfo().GetSSU2V6Address(); + if (addr && addr->ssu && addr->ssu->introducers.empty()) + i2p::context.SetUnreachableSSU2(false, true); // v6 + + UpdateIntroducers(false); + ScheduleIntroducersUpdateTimerV6(); + } + } + } + } } diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 8ad64692..df20d90b 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -13,133 +13,169 @@ #include "util.h" #include "SSU2Session.h" -namespace i2p -{ -namespace transport -{ - const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds - const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds - const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K - const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K - const size_t SSU2_MAX_NUM_INTRODUCERS = 3; - const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour - 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; - }; +namespace i2p { + namespace transport { + const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds + const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds + const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU2_MAX_NUM_INTRODUCERS = 3; + const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour + const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes + const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds - class ReceiveService: public i2p::util::RunnableService - { - public: + class SSU2Server : private i2p::util::RunnableServiceWithWork { + struct Packet { + uint8_t buf[SSU2_MAX_PACKET_SIZE]; + size_t len; + boost::asio::ip::udp::endpoint from; + }; - ReceiveService (const std::string& name): RunnableService (name) {}; - boost::asio::io_service& GetService () { return GetIOService (); }; - void Start () { StartIOService (); }; - void Stop () { StopIOService (); }; - }; + class ReceiveService : public i2p::util::RunnableService { + public: - public: + ReceiveService(const std::string &name) : RunnableService(name) {}; - SSU2Server (); - ~SSU2Server () {}; + boost::asio::io_service &GetService() { return GetIOService(); }; - void Start (); - 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 session); - void RemoveSession (uint64_t connID); - void AddSessionByRouterHash (std::shared_ptr session); - bool AddPendingOutgoingSession (std::shared_ptr session); - void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); - std::shared_ptr FindSession (const i2p::data::IdentHash& ident) const; - std::shared_ptr FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; - std::shared_ptr GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, - const i2p::data::IdentHash& excluded) const; - - void AddRelay (uint32_t tag, std::shared_ptr relay); - void RemoveRelay (uint32_t tag); - std::shared_ptr FindRelaySession (uint32_t tag); + void Start() { StartIOService(); }; - 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); + void Stop() { StopIOService(); }; + }; - bool CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest = false); - bool StartPeerTest (std::shared_ptr 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 NewIncomingToken (const boost::asio::ip::udp::endpoint& ep); - - void RescheduleIntroducersUpdateTimer (); - void RescheduleIntroducersUpdateTimerV6 (); + public: - i2p::util::MemoryPool& GetSentPacketsPool () { return m_SentPacketsPool; }; - - private: + SSU2Server(); - 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 packets); - void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + ~SSU2Server() {}; - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); + void Start(); - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); + void Stop(); - void ConnectThroughIntroducer (std::shared_ptr session); - std::list > FindIntroducers (int maxNumIntroducers, - bool v4, const std::set& excluded) const; - void UpdateIntroducers (bool v4); - void ScheduleIntroducersUpdateTimer (); - void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); - void ScheduleIntroducersUpdateTimerV6 (); - - private: + boost::asio::io_service &GetService() { return GetIOService(); }; - ReceiveService m_ReceiveService; - boost::asio::ip::udp::socket m_SocketV4, m_SocketV6; - boost::asio::ip::address m_AddressV4, m_AddressV6; - std::unordered_map > m_Sessions; - std::unordered_map > m_SessionsByRouterHash; - std::map > m_PendingOutgoingSessions; - std::map > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) - std::map > m_Relays; // we are introducer, relay tag -> session - std::list m_Introducers, m_IntroducersV6; // introducers we are connected to - i2p::util::MemoryPoolMt m_PacketsPool; - i2p::util::MemoryPool m_SentPacketsPool; - boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer, - m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6; - std::shared_ptr m_LastSession; - bool m_IsPublished; // if we maintain introducers - bool m_IsSyncClockFromPeers; - - public: + void SetLocalAddress(const boost::asio::ip::address &localAddress); - // for HTTP/I2PControl - const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; }; - }; -} + 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 session); + + void RemoveSession(uint64_t connID); + + void AddSessionByRouterHash(std::shared_ptr session); + + bool AddPendingOutgoingSession(std::shared_ptr session); + + void RemovePendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep); + + std::shared_ptr FindSession(const i2p::data::IdentHash &ident) const; + + std::shared_ptr FindPendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep) const; + + std::shared_ptr GetRandomSession(i2p::data::RouterInfo::CompatibleTransports remoteTransports, + const i2p::data::IdentHash &excluded) const; + + void AddRelay(uint32_t tag, std::shared_ptr relay); + + void RemoveRelay(uint32_t tag); + + std::shared_ptr 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 router, + std::shared_ptr address, bool peerTest = false); + + bool StartPeerTest(std::shared_ptr 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 NewIncomingToken(const boost::asio::ip::udp::endpoint &ep); + + void RescheduleIntroducersUpdateTimer(); + + void RescheduleIntroducersUpdateTimerV6(); + + i2p::util::MemoryPool &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 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 session); + + std::list > FindIntroducers(int maxNumIntroducers, + bool v4, + const std::set &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 > m_Sessions; + std::unordered_map > m_SessionsByRouterHash; + std::map > m_PendingOutgoingSessions; + std::map > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) + std::map > m_Relays; // we are introducer, relay tag -> session + std::list m_Introducers, m_IntroducersV6; // introducers we are connected to + i2p::util::MemoryPoolMt m_PacketsPool; + i2p::util::MemoryPool m_SentPacketsPool; + boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer, + m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6; + std::shared_ptr 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 diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index b66c44ef..f1f7b943 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -14,2670 +14,2420 @@ #include "NetDb.hpp" #include "SSU2.h" -namespace i2p -{ -namespace transport -{ - void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) - { - if (msg->len + fragmentSize > msg->maxLen) - { - LogPrint (eLogInfo, "SSU2: I2NP message size ", msg->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (); - *newMsg = *msg; - msg = newMsg; - } - if (msg->Concat (fragment, fragmentSize) < fragmentSize) - LogPrint (eLogError, "SSU2: I2NP buffer overflow ", msg->maxLen); - nextFragmentNum++; - } - - - SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter, - std::shared_ptr addr): - TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), - m_Server (server), m_Address (addr), m_RemoteTransports (0), - m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), - m_SendPacketNum (0), m_ReceivePacketNum (0), m_IsDataReceived (false), - m_WindowSize (SSU2_MIN_WINDOW_SIZE), m_RTT (SSU2_RESEND_INTERVAL), - m_RTO (SSU2_RESEND_INTERVAL*SSU2_kAPPA), m_RelayTag (0), - m_ConnectTimer (server.GetService ()), m_TerminationReason (eSSU2TerminationReasonNormalClose), - m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32) // min size - { - m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); - if (in_RemoteRouter && m_Address) - { - // outgoing - InitNoiseXKState1 (*m_NoiseState, m_Address->s); - m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); - m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); - RAND_bytes ((uint8_t *)&m_DestConnID, 8); - RAND_bytes ((uint8_t *)&m_SourceConnID, 8); - } - else - { - // incoming - InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ()); - } - } +namespace i2p { + namespace transport { + void SSU2IncompleteMessage::AttachNextFragment(const uint8_t *fragment, size_t fragmentSize) { + if (msg->len + fragmentSize > msg->maxLen) { + LogPrint(eLogInfo, "SSU2: I2NP message size ", msg->maxLen, " is not enough"); + auto newMsg = NewI2NPMessage(); + *newMsg = *msg; + msg = newMsg; + } + if (msg->Concat(fragment, fragmentSize) < fragmentSize) + LogPrint(eLogError, "SSU2: I2NP buffer overflow ", msg->maxLen); + nextFragmentNum++; + } - SSU2Session::~SSU2Session () - { - } - void SSU2Session::Connect () - { - if (m_State == eSSU2SessionStateUnknown || m_State == eSSU2SessionStateTokenReceived) - { - ScheduleConnectTimer (); - auto token = m_Server.FindOutgoingToken (m_RemoteEndpoint); - if (token) - SendSessionRequest (token); - else - { - m_State = eSSU2SessionStateUnknown; - SendTokenRequest (); - } - } - } + SSU2Session::SSU2Session(SSU2Server &server, std::shared_ptr in_RemoteRouter, + std::shared_ptr addr) : + TransportSession(in_RemoteRouter, SSU2_CONNECT_TIMEOUT), + m_Server(server), m_Address(addr), m_RemoteTransports(0), + m_DestConnID(0), m_SourceConnID(0), m_State(eSSU2SessionStateUnknown), + m_SendPacketNum(0), m_ReceivePacketNum(0), m_IsDataReceived(false), + m_WindowSize(SSU2_MIN_WINDOW_SIZE), m_RTT(SSU2_RESEND_INTERVAL), + m_RTO(SSU2_RESEND_INTERVAL * SSU2_kAPPA), m_RelayTag(0), + m_ConnectTimer(server.GetService()), m_TerminationReason(eSSU2TerminationReasonNormalClose), + m_MaxPayloadSize(SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32) // min size + { + m_NoiseState.reset(new i2p::crypto::NoiseSymmetricState); + if (in_RemoteRouter && m_Address) { + // outgoing + InitNoiseXKState1(*m_NoiseState, m_Address->s); + m_RemoteEndpoint = boost::asio::ip::udp::endpoint(m_Address->host, m_Address->port); + m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports(false); + RAND_bytes((uint8_t * ) & m_DestConnID, 8); + RAND_bytes((uint8_t * ) & m_SourceConnID, 8); + } else { + // incoming + InitNoiseXKState1(*m_NoiseState, i2p::context.GetSSU2StaticPublicKey()); + } + } - void SSU2Session::ScheduleConnectTimer () - { - m_ConnectTimer.cancel (); - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU2_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSU2Session::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } + SSU2Session::~SSU2Session() { + } - void SSU2Session::HandleConnectTimer (const boost::system::error_code& ecode) - { - if (!ecode) - { - // timeout expired - LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds"); - Terminate (); - } - } - - bool SSU2Session::Introduce (std::shared_ptr session, uint32_t relayTag) - { - // we are Alice - if (!session || !relayTag) return false; - // find local adddress to introduce - auto localAddress = session->FindLocalAddress (); - if (!localAddress) return false; - // create nonce - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - auto ts = i2p::util::GetSecondsSinceEpoch (); - // payload - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - payload[0] = eSSU2BlkRelayRequest; - payload[3] = 0; // flag - htobe32buf (payload + 4, nonce); - htobe32buf (payload + 8, relayTag); - htobe32buf (payload + 12, ts); - payload[16] = 2; // ver - size_t asz = CreateEndpoint (payload + 18, m_MaxPayloadSize - 18, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); - if (!asz) return false; - payload[17] = asz; - payloadSize += asz + 18; - SignedData s; - s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash - s.Insert (payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint - s.Sign (i2p::context.GetPrivateKeys (), payload + payloadSize); - payloadSize += i2p::context.GetIdentity ()->GetSignatureLen (); - htobe16buf (payload + 1, payloadSize - 3); // size - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // send - m_RelaySessions.emplace (nonce, std::make_pair (session, ts)); - session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce); - session->m_DestConnID = ~session->m_SourceConnID; - m_Server.AddSession (session); - SendData (payload, payloadSize); + void SSU2Session::Connect() { + if (m_State == eSSU2SessionStateUnknown || m_State == eSSU2SessionStateTokenReceived) { + ScheduleConnectTimer(); + auto token = m_Server.FindOutgoingToken(m_RemoteEndpoint); + if (token) + SendSessionRequest(token); + else { + m_State = eSSU2SessionStateUnknown; + SendTokenRequest(); + } + } + } - return true; - } + void SSU2Session::ScheduleConnectTimer() { + m_ConnectTimer.cancel(); + m_ConnectTimer.expires_from_now(boost::posix_time::seconds(SSU2_CONNECT_TIMEOUT)); + m_ConnectTimer.async_wait(std::bind(&SSU2Session::HandleConnectTimer, + shared_from_this(), std::placeholders::_1)); + } - void SSU2Session::WaitForIntroduction () - { - m_State = eSSU2SessionStateIntroduced; - ScheduleConnectTimer (); - } + void SSU2Session::HandleConnectTimer(const boost::system::error_code &ecode) { + if (!ecode) { + // timeout expired + LogPrint(eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", + SSU2_CONNECT_TIMEOUT, " seconds"); + Terminate(); + } + } - void SSU2Session::ConnectAfterIntroduction () - { - if (m_State == eSSU2SessionStateIntroduced) - { - // create new connID - uint64_t oldConnID = GetConnID (); - RAND_bytes ((uint8_t *)&m_DestConnID, 8); - RAND_bytes ((uint8_t *)&m_SourceConnID, 8); - // connect - m_State = eSSU2SessionStateTokenReceived; - m_Server.AddPendingOutgoingSession (shared_from_this ()); - m_Server.RemoveSession (oldConnID); - Connect (); - } - } - - void SSU2Session::SendPeerTest () - { - // we are Alice - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - auto ts = i2p::util::GetSecondsSinceEpoch (); - // session for message 5 - auto session = std::make_shared (m_Server); - session->SetState (eSSU2SessionStatePeerTest); - m_PeerTests.emplace (nonce, std::make_pair (session, ts)); - session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce); - session->m_DestConnID = ~session->m_SourceConnID; - m_Server.AddSession (session); - // peer test block - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, nonce); - if (payloadSize > 0) - { - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - } - } + bool SSU2Session::Introduce(std::shared_ptr session, uint32_t relayTag) { + // we are Alice + if (!session || !relayTag) return false; + // find local adddress to introduce + auto localAddress = session->FindLocalAddress(); + if (!localAddress) return false; + // create nonce + uint32_t nonce; + RAND_bytes((uint8_t * ) & nonce, 4); + auto ts = i2p::util::GetSecondsSinceEpoch(); + // payload + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = 0; + payload[0] = eSSU2BlkRelayRequest; + payload[3] = 0; // flag + htobe32buf(payload + 4, nonce); + htobe32buf(payload + 8, relayTag); + htobe32buf(payload + 12, ts); + payload[16] = 2; // ver + size_t asz = CreateEndpoint(payload + 18, m_MaxPayloadSize - 18, + boost::asio::ip::udp::endpoint(localAddress->host, localAddress->port)); + if (!asz) return false; + payload[17] = asz; + payloadSize += asz + 18; + SignedData s; + s.Insert((const uint8_t *) "RelayRequestData", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(session->GetRemoteIdentity()->GetIdentHash(), 32); // chash + s.Insert(payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint + s.Sign(i2p::context.GetPrivateKeys(), payload + payloadSize); + payloadSize += i2p::context.GetIdentity()->GetSignatureLen(); + htobe16buf(payload + 1, payloadSize - 3); // size + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + // send + m_RelaySessions.emplace(nonce, std::make_pair(session, ts)); + session->m_SourceConnID = htobe64(((uint64_t) nonce << 32) | nonce); + session->m_DestConnID = ~session->m_SourceConnID; + m_Server.AddSession(session); + SendData(payload, payloadSize); - void SSU2Session::SendKeepAlive () - { - if (IsEstablished ()) - { - uint8_t payload[20]; - size_t payloadSize = CreatePaddingBlock (payload, 20, 5); - SendData (payload, payloadSize); - } - } - - void SSU2Session::Terminate () - { - if (m_State != eSSU2SessionStateTerminated) - { - m_State = eSSU2SessionStateTerminated; - m_ConnectTimer.cancel (); - m_OnEstablished = nullptr; - if (m_RelayTag) - m_Server.RemoveRelay (m_RelayTag); - m_SentHandshakePacket.reset (nullptr); - m_SendQueue.clear (); - m_SentPackets.clear (); - m_IncompleteMessages.clear (); - m_RelaySessions.clear (); - m_PeerTests.clear (); - m_Server.RemoveSession (m_SourceConnID); - transports.PeerDisconnected (shared_from_this ()); - LogPrint (eLogDebug, "SSU2: Session terminated"); - } - } + return true; + } - void SSU2Session::RequestTermination (SSU2TerminationReason reason) - { - if (m_State == eSSU2SessionStateEstablished || m_State == eSSU2SessionStateClosing) - { - m_TerminationReason = reason; - SendTermination (); - } - m_State = eSSU2SessionStateClosing; - } - - void SSU2Session::Established () - { - m_State = eSSU2SessionStateEstablished; - m_EphemeralKeys = nullptr; - m_NoiseState.reset (nullptr); - m_SessionConfirmedFragment.reset (nullptr); - m_SentHandshakePacket.reset (nullptr); - m_ConnectTimer.cancel (); - SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT); - transports.PeerConnected (shared_from_this ()); - if (m_OnEstablished) - { - m_OnEstablished (); - m_OnEstablished = nullptr; - } - } + void SSU2Session::WaitForIntroduction() { + m_State = eSSU2SessionStateIntroduced; + ScheduleConnectTimer(); + } - void SSU2Session::Done () - { - m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, shared_from_this ())); - } + void SSU2Session::ConnectAfterIntroduction() { + if (m_State == eSSU2SessionStateIntroduced) { + // create new connID + uint64_t oldConnID = GetConnID(); + RAND_bytes((uint8_t * ) & m_DestConnID, 8); + RAND_bytes((uint8_t * ) & m_SourceConnID, 8); + // connect + m_State = eSSU2SessionStateTokenReceived; + m_Server.AddPendingOutgoingSession(shared_from_this()); + m_Server.RemoveSession(oldConnID); + Connect(); + } + } - void SSU2Session::SendLocalRouterInfo (bool update) - { - if (update || !IsOutgoing ()) - { - auto s = shared_from_this (); - m_Server.GetService ().post ([s]() - { - if (!s->IsEstablished ()) return; - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.GetSharedRouterInfo ()); - if (payloadSize) - { - if (payloadSize < s->m_MaxPayloadSize) - payloadSize += s->CreatePaddingBlock (payload + payloadSize, s->m_MaxPayloadSize - payloadSize); - s->SendData (payload, payloadSize); - } - else - s->SendFragmentedMessage (CreateDatabaseStoreMsg ()); - }); - } + void SSU2Session::SendPeerTest() { + // we are Alice + uint32_t nonce; + RAND_bytes((uint8_t * ) & nonce, 4); + auto ts = i2p::util::GetSecondsSinceEpoch(); + // session for message 5 + auto session = std::make_shared(m_Server); + session->SetState(eSSU2SessionStatePeerTest); + m_PeerTests.emplace(nonce, std::make_pair(session, ts)); + session->m_SourceConnID = htobe64(((uint64_t) nonce << 32) | nonce); + session->m_DestConnID = ~session->m_SourceConnID; + m_Server.AddSession(session); + // peer test block + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreatePeerTestBlock(payload, m_MaxPayloadSize, nonce); + if (payloadSize > 0) { + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData(payload, payloadSize); + } + } - } - - void SSU2Session::SendI2NPMessages (const std::vector >& msgs) - { - m_Server.GetService ().post (std::bind (&SSU2Session::PostI2NPMessages, shared_from_this (), msgs)); - } + void SSU2Session::SendKeepAlive() { + if (IsEstablished()) { + uint8_t payload[20]; + size_t payloadSize = CreatePaddingBlock(payload, 20, 5); + SendData(payload, payloadSize); + } + } - void SSU2Session::PostI2NPMessages (std::vector > msgs) - { - if (m_State == eSSU2SessionStateTerminated) return; - for (auto it: msgs) - m_SendQueue.push_back (it); - SendQueue (); + void SSU2Session::Terminate() { + if (m_State != eSSU2SessionStateTerminated) { + m_State = eSSU2SessionStateTerminated; + m_ConnectTimer.cancel(); + m_OnEstablished = nullptr; + if (m_RelayTag) + m_Server.RemoveRelay(m_RelayTag); + m_SentHandshakePacket.reset(nullptr); + m_SendQueue.clear(); + m_SentPackets.clear(); + m_IncompleteMessages.clear(); + m_RelaySessions.clear(); + m_PeerTests.clear(); + m_Server.RemoveSession(m_SourceConnID); + transports.PeerDisconnected(shared_from_this()); + LogPrint(eLogDebug, "SSU2: Session terminated"); + } + } - if (m_SendQueue.size () > 0) // windows is full - { - if (m_SendQueue.size () <= SSU2_MAX_OUTGOING_QUEUE_SIZE) - Resend (i2p::util::GetMillisecondsSinceEpoch ()); - else - { - LogPrint (eLogWarning, "SSU2: Outgoing messages queue size to ", - GetIdentHashBase64(), " exceeds ", SSU2_MAX_OUTGOING_QUEUE_SIZE); - RequestTermination (eSSU2TerminationReasonTimeout); - } - } - } + void SSU2Session::RequestTermination(SSU2TerminationReason reason) { + if (m_State == eSSU2SessionStateEstablished || m_State == eSSU2SessionStateClosing) { + m_TerminationReason = reason; + SendTermination(); + } + m_State = eSSU2SessionStateClosing; + } - bool SSU2Session::SendQueue () - { - if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - size_t ackBlockSize = CreateAckBlock (packet->payload, m_MaxPayloadSize); - bool ackBlockSent = false; - packet->payloadSize += ackBlockSize; - while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) - { - auto msg = m_SendQueue.front (); - size_t len = msg->GetNTCP2Length () + 3; - if (len > m_MaxPayloadSize) // message too long - { - m_SendQueue.pop_front (); - if (SendFragmentedMessage (msg)) - ackBlockSent = true; - } - else if (packet->payloadSize + len <= m_MaxPayloadSize) - { - m_SendQueue.pop_front (); - packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, std::move (msg)); - } - else - { - // create new packet and copy ack block - auto newPacket = m_Server.GetSentPacketsPool ().AcquireShared (); - memcpy (newPacket->payload, packet->payload, ackBlockSize); - newPacket->payloadSize = ackBlockSize; - // complete current packet - if (packet->payloadSize > ackBlockSize) // more than just ack block - { - ackBlockSent = true; - // try to add padding - if (packet->payloadSize + 16 < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - } - else - { - // reduce ack block - if (len + 8 < m_MaxPayloadSize) - { - // keep Ack block and drop some ranges - ackBlockSent = true; - packet->payloadSize = m_MaxPayloadSize - len; - if (packet->payloadSize & 0x01) packet->payloadSize--; // make it even - htobe16buf (packet->payload + 1, packet->payloadSize - 3); // new block size - } - else // drop Ack block completely - packet->payloadSize = 0; - // msg fits single packet - m_SendQueue.pop_front (); - packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, std::move (msg)); - } - // send right a way - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - packet = newPacket; // just ack block - } - }; - if (packet->payloadSize > ackBlockSize) - { - ackBlockSent = true; - if (packet->payloadSize + 16 < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - } - return ackBlockSent; - } - return false; - } + void SSU2Session::Established() { + m_State = eSSU2SessionStateEstablished; + m_EphemeralKeys = nullptr; + m_NoiseState.reset(nullptr); + m_SessionConfirmedFragment.reset(nullptr); + m_SentHandshakePacket.reset(nullptr); + m_ConnectTimer.cancel(); + SetTerminationTimeout(SSU2_TERMINATION_TIMEOUT); + transports.PeerConnected(shared_from_this()); + if (m_OnEstablished) { + m_OnEstablished(); + m_OnEstablished = nullptr; + } + } - bool SSU2Session::SendFragmentedMessage (std::shared_ptr msg) - { - size_t lastFragmentSize = (msg->GetNTCP2Length () + 3 - m_MaxPayloadSize) % (m_MaxPayloadSize - 8); - size_t extraSize = m_MaxPayloadSize - lastFragmentSize; - bool ackBlockSent = false; - uint32_t msgID; - memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - if (extraSize >= 8) - { - packet->payloadSize = CreateAckBlock (packet->payload, extraSize); - ackBlockSent = true; - if (packet->payloadSize + 12 < m_MaxPayloadSize) - { - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - packet = m_Server.GetSentPacketsPool ().AcquireShared (); - } - else - extraSize -= packet->payloadSize; - } - size_t offset = extraSize > 0 ? (rand () % extraSize) : 0; - if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0; - auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - offset - packet->payloadSize, msg); - if (!size) return false; - extraSize -= offset; - packet->payloadSize += size; - uint32_t firstPacketNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (firstPacketNum, packet); - uint8_t fragmentNum = 0; - while (msg->offset < msg->len) - { - offset = extraSize > 0 ? (rand () % extraSize) : 0; - packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, m_MaxPayloadSize - offset, msg, fragmentNum, msgID); - extraSize -= offset; - if (msg->offset >= msg->len && packet->payloadSize + 16 < m_MaxPayloadSize) // last fragment - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t followonPacketNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (followonPacketNum, packet); - } - return ackBlockSent; - } + void SSU2Session::Done() { + m_Server.GetService().post(std::bind(&SSU2Session::Terminate, shared_from_this())); + } - void SSU2Session::Resend (uint64_t ts) - { - // resend handshake packet - if (m_SentHandshakePacket && ts >= m_SentHandshakePacket->sendTime + SSU2_HANDSHAKE_RESEND_INTERVAL) - { - LogPrint (eLogDebug, "SSU2: Resending ", (int)m_State); - ResendHandshakePacket (); - m_SentHandshakePacket->sendTime = ts; - return; - } - // resend data packets - if (m_SentPackets.empty ()) return; - std::map > resentPackets; - for (auto it = m_SentPackets.begin (); it != m_SentPackets.end (); ) - if (ts >= it->second->sendTime + it->second->numResends*m_RTO) - { - if (it->second->numResends > SSU2_MAX_NUM_RESENDS) - { - LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session"); - m_SentPackets.clear (); - m_SendQueue.clear (); - RequestTermination (eSSU2TerminationReasonTimeout); - return; - } - else - { - uint32_t packetNum = SendData (it->second->payload, it->second->payloadSize); - it->second->numResends++; - it->second->sendTime = ts; - resentPackets.emplace (packetNum, it->second); - it = m_SentPackets.erase (it); - } - } - else - it++; - if (!resentPackets.empty ()) - { + void SSU2Session::SendLocalRouterInfo(bool update) { + if (update || !IsOutgoing()) { + auto s = shared_from_this(); + m_Server.GetService().post([s]() { + if (!s->IsEstablished()) return; + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = s->CreateRouterInfoBlock(payload, s->m_MaxPayloadSize - 32, + i2p::context.GetSharedRouterInfo()); + if (payloadSize) { + if (payloadSize < s->m_MaxPayloadSize) + payloadSize += s->CreatePaddingBlock(payload + payloadSize, + s->m_MaxPayloadSize - payloadSize); + s->SendData(payload, payloadSize); + } else + s->SendFragmentedMessage(CreateDatabaseStoreMsg()); + }); + } + + } + + void SSU2Session::SendI2NPMessages(const std::vector > &msgs) { + m_Server.GetService().post(std::bind(&SSU2Session::PostI2NPMessages, shared_from_this(), msgs)); + } + + void SSU2Session::PostI2NPMessages(std::vector > msgs) { + if (m_State == eSSU2SessionStateTerminated) return; + for (auto it: msgs) + m_SendQueue.push_back(it); + SendQueue(); + + if (m_SendQueue.size() > 0) // windows is full + { + if (m_SendQueue.size() <= SSU2_MAX_OUTGOING_QUEUE_SIZE) + Resend(i2p::util::GetMillisecondsSinceEpoch()); + else { + LogPrint(eLogWarning, "SSU2: Outgoing messages queue size to ", + GetIdentHashBase64(), " exceeds ", SSU2_MAX_OUTGOING_QUEUE_SIZE); + RequestTermination(eSSU2TerminationReasonTimeout); + } + } + } + + bool SSU2Session::SendQueue() { + if (!m_SendQueue.empty() && m_SentPackets.size() <= m_WindowSize) { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + auto packet = m_Server.GetSentPacketsPool().AcquireShared(); + size_t ackBlockSize = CreateAckBlock(packet->payload, m_MaxPayloadSize); + bool ackBlockSent = false; + packet->payloadSize += ackBlockSize; + while (!m_SendQueue.empty() && m_SentPackets.size() <= m_WindowSize) { + auto msg = m_SendQueue.front(); + size_t len = msg->GetNTCP2Length() + 3; + if (len > m_MaxPayloadSize) // message too long + { + m_SendQueue.pop_front(); + if (SendFragmentedMessage(msg)) + ackBlockSent = true; + } else if (packet->payloadSize + len <= m_MaxPayloadSize) { + m_SendQueue.pop_front(); + packet->payloadSize += CreateI2NPBlock(packet->payload + packet->payloadSize, + m_MaxPayloadSize - packet->payloadSize, std::move(msg)); + } else { + // create new packet and copy ack block + auto newPacket = m_Server.GetSentPacketsPool().AcquireShared(); + memcpy(newPacket->payload, packet->payload, ackBlockSize); + newPacket->payloadSize = ackBlockSize; + // complete current packet + if (packet->payloadSize > ackBlockSize) // more than just ack block + { + ackBlockSent = true; + // try to add padding + if (packet->payloadSize + 16 < m_MaxPayloadSize) + packet->payloadSize += CreatePaddingBlock(packet->payload + packet->payloadSize, + m_MaxPayloadSize - packet->payloadSize); + } else { + // reduce ack block + if (len + 8 < m_MaxPayloadSize) { + // keep Ack block and drop some ranges + ackBlockSent = true; + packet->payloadSize = m_MaxPayloadSize - len; + if (packet->payloadSize & 0x01) packet->payloadSize--; // make it even + htobe16buf(packet->payload + 1, packet->payloadSize - 3); // new block size + } else // drop Ack block completely + packet->payloadSize = 0; + // msg fits single packet + m_SendQueue.pop_front(); + packet->payloadSize += CreateI2NPBlock(packet->payload + packet->payloadSize, + m_MaxPayloadSize - packet->payloadSize, + std::move(msg)); + } + // send right a way + uint32_t packetNum = SendData(packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace(packetNum, packet); + packet = newPacket; // just ack block + } + }; + if (packet->payloadSize > ackBlockSize) { + ackBlockSent = true; + if (packet->payloadSize + 16 < m_MaxPayloadSize) + packet->payloadSize += CreatePaddingBlock(packet->payload + packet->payloadSize, + m_MaxPayloadSize - packet->payloadSize); + uint32_t packetNum = SendData(packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace(packetNum, packet); + } + return ackBlockSent; + } + return false; + } + + bool SSU2Session::SendFragmentedMessage(std::shared_ptr msg) { + size_t lastFragmentSize = (msg->GetNTCP2Length() + 3 - m_MaxPayloadSize) % (m_MaxPayloadSize - 8); + size_t extraSize = m_MaxPayloadSize - lastFragmentSize; + bool ackBlockSent = false; + uint32_t msgID; + memcpy(&msgID, msg->GetHeader() + I2NP_HEADER_MSGID_OFFSET, 4); + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + auto packet = m_Server.GetSentPacketsPool().AcquireShared(); + if (extraSize >= 8) { + packet->payloadSize = CreateAckBlock(packet->payload, extraSize); + ackBlockSent = true; + if (packet->payloadSize + 12 < m_MaxPayloadSize) { + uint32_t packetNum = SendData(packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace(packetNum, packet); + packet = m_Server.GetSentPacketsPool().AcquireShared(); + } else + extraSize -= packet->payloadSize; + } + size_t offset = extraSize > 0 ? (rand() % extraSize) : 0; + if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0; + auto size = CreateFirstFragmentBlock(packet->payload + packet->payloadSize, + m_MaxPayloadSize - offset - packet->payloadSize, msg); + if (!size) return false; + extraSize -= offset; + packet->payloadSize += size; + uint32_t firstPacketNum = SendData(packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace(firstPacketNum, packet); + uint8_t fragmentNum = 0; + while (msg->offset < msg->len) { + offset = extraSize > 0 ? (rand() % extraSize) : 0; + packet = m_Server.GetSentPacketsPool().AcquireShared(); + packet->payloadSize = CreateFollowOnFragmentBlock(packet->payload, m_MaxPayloadSize - offset, msg, + fragmentNum, msgID); + extraSize -= offset; + if (msg->offset >= msg->len && packet->payloadSize + 16 < m_MaxPayloadSize) // last fragment + packet->payloadSize += CreatePaddingBlock(packet->payload + packet->payloadSize, + m_MaxPayloadSize - packet->payloadSize); + uint32_t followonPacketNum = SendData(packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace(followonPacketNum, packet); + } + return ackBlockSent; + } + + void SSU2Session::Resend(uint64_t ts) { + // resend handshake packet + if (m_SentHandshakePacket && ts >= m_SentHandshakePacket->sendTime + SSU2_HANDSHAKE_RESEND_INTERVAL) { + LogPrint(eLogDebug, "SSU2: Resending ", (int) m_State); + ResendHandshakePacket(); + m_SentHandshakePacket->sendTime = ts; + return; + } + // resend data packets + if (m_SentPackets.empty()) return; + std::map > resentPackets; + for (auto it = m_SentPackets.begin(); it != m_SentPackets.end();) + if (ts >= it->second->sendTime + it->second->numResends * m_RTO) { + if (it->second->numResends > SSU2_MAX_NUM_RESENDS) { + LogPrint(eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, + " attempts. Terminate session"); + m_SentPackets.clear(); + m_SendQueue.clear(); + RequestTermination(eSSU2TerminationReasonTimeout); + return; + } else { + uint32_t packetNum = SendData(it->second->payload, it->second->payloadSize); + it->second->numResends++; + it->second->sendTime = ts; + resentPackets.emplace(packetNum, it->second); + it = m_SentPackets.erase(it); + } + } else + it++; + if (!resentPackets.empty()) { #if (__cplusplus >= 201703L) // C++ 17 or higher - m_SentPackets.merge (resentPackets); + m_SentPackets.merge (resentPackets); #else - m_SentPackets.insert (resentPackets.begin (), resentPackets.end ()); + m_SentPackets.insert(resentPackets.begin(), resentPackets.end()); #endif - m_WindowSize >>= 1; // /2 - if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE; - } - } + m_WindowSize >>= 1; // /2 + if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE; + } + } - void SSU2Session::ResendHandshakePacket () - { - if (m_SentHandshakePacket) - { - m_Server.Send (m_SentHandshakePacket->header.buf, 16, m_SentHandshakePacket->headerX, 48, - m_SentHandshakePacket->payload, m_SentHandshakePacket->payloadSize, m_RemoteEndpoint); - if (m_SessionConfirmedFragment && m_State == eSSU2SessionStateSessionConfirmedSent) - // resend second fragment of SessionConfirmed - m_Server.Send (m_SessionConfirmedFragment->header.buf, 16, - m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); - } - } - - bool SSU2Session::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - // we are Bob - m_SourceConnID = connID; - Header header; - header.h.connID = connID; - memcpy (header.buf + 8, buf + 8, 8); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - switch (header.h.type) - { - case eSSU2SessionRequest: - ProcessSessionRequest (header, buf, len); - break; - case eSSU2TokenRequest: - ProcessTokenRequest (header, buf, len); - break; - case eSSU2PeerTest: - { - // TODO: remove later - const uint8_t nonce[12] = {0}; - uint64_t headerX[2]; - i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - LogPrint (eLogWarning, "SSU2: Unexpected PeerTest message SourceConnID=", connID, " DestConnID=", headerX[0]); - break; - } - case eSSU2HolePunch: - LogPrint (eLogDebug, "SSU2: Late HolePunch for ", connID); - break; - default: - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " from ", m_RemoteEndpoint, " of ", len, " bytes"); - return false; - } - } - return true; - } + void SSU2Session::ResendHandshakePacket() { + if (m_SentHandshakePacket) { + m_Server.Send(m_SentHandshakePacket->header.buf, 16, m_SentHandshakePacket->headerX, 48, + m_SentHandshakePacket->payload, m_SentHandshakePacket->payloadSize, m_RemoteEndpoint); + if (m_SessionConfirmedFragment && m_State == eSSU2SessionStateSessionConfirmedSent) + // resend second fragment of SessionConfirmed + m_Server.Send(m_SessionConfirmedFragment->header.buf, 16, + m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, + m_RemoteEndpoint); + } + } - void SSU2Session::SendSessionRequest (uint64_t token) - { - // we are Alice - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - m_SentHandshakePacket.reset (new HandshakePacket); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - m_SentHandshakePacket->sendTime = ts; - - Header& header = m_SentHandshakePacket->header; - uint8_t * headerX = m_SentHandshakePacket->headerX, - * payload = m_SentHandshakePacket->payload; - // fill packet - header.h.connID = m_DestConnID; // dest id - header.h.packetNum = 0; - header.h.type = eSSU2SessionRequest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (headerX, &m_SourceConnID, 8); // source id - memcpy (headerX + 8, &token, 8); // token - memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // X - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, ts/1000); - size_t payloadSize = 7; - if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ()) - { - // relay tag request - payload[payloadSize] = eSSU2BlkRelayTagRequest; - memset (payload + payloadSize + 1, 0, 2); // size = 0 - payloadSize += 3; - } - payloadSize += CreatePaddingBlock (payload + payloadSize, 40 - payloadSize, 1); - // KDF for session request - m_NoiseState->MixHash ({ {header.buf, 16}, {headerX, 16} }); // h = SHA256(h || header) - m_NoiseState->MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk); - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (m_Address->s, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // encrypt - const uint8_t nonce[12] = {0}; - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); - i2p::crypto::ChaCha20 (headerX, 48, m_Address->i, nonce, headerX); - m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated - m_SentHandshakePacket->payloadSize = payloadSize; - // send - if (m_State == eSSU2SessionStateTokenReceived || m_Server.AddPendingOutgoingSession (shared_from_this ())) - { - m_State = eSSU2SessionStateSessionRequestSent; - m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); - } - else - { - LogPrint (eLogWarning, "SSU2: SessionRequest request to ", m_RemoteEndpoint, " already pending"); - Terminate (); - } - } + bool SSU2Session::ProcessFirstIncomingMessage(uint64_t connID, uint8_t *buf, size_t len) { + // we are Bob + m_SourceConnID = connID; + Header header; + header.h.connID = connID; + memcpy(header.buf + 8, buf + 8, 8); + header.ll[1] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 12)); + switch (header.h.type) { + case eSSU2SessionRequest: + ProcessSessionRequest(header, buf, len); + break; + case eSSU2TokenRequest: + ProcessTokenRequest(header, buf, len); + break; + case eSSU2PeerTest: { + // TODO: remove later + const uint8_t nonce[12] = {0}; + uint64_t headerX[2]; + i2p::crypto::ChaCha20(buf + 16, 16, i2p::context.GetSSU2IntroKey(), nonce, (uint8_t *) headerX); + LogPrint(eLogWarning, "SSU2: Unexpected PeerTest message SourceConnID=", connID, " DestConnID=", + headerX[0]); + break; + } + case eSSU2HolePunch: + LogPrint(eLogDebug, "SSU2: Late HolePunch for ", connID); + break; + default: { + LogPrint(eLogWarning, "SSU2: Unexpected message type ", (int) header.h.type, " from ", + m_RemoteEndpoint, " of ", len, " bytes"); + return false; + } + } + return true; + } - void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len) - { - // we are Bob - const uint8_t nonce[12] = {0}; - uint8_t headerX[48]; - i2p::crypto::ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); - memcpy (&m_DestConnID, headerX, 8); - uint64_t token; - memcpy (&token, headerX + 8, 8); - if (!token || token != m_Server.GetIncomingToken (m_RemoteEndpoint)) - { - LogPrint (eLogDebug, "SSU2: SessionRequest token mismatch. Retry"); - SendRetry (); - return; - } - // KDF for session request - m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) - m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || aepk); - uint8_t sharedSecret[32]; - i2p::context.GetSSU2StaticKeys ().Agree (headerX + 16, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // decrypt - uint8_t * payload = buf + 64; - std::vector decryptedPayload(len - 80); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) - { - LogPrint (eLogWarning, "SSU2: SessionRequest AEAD verification failed "); - return; - } - m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated - // payload - m_State = eSSU2SessionStateSessionRequestReceived; - HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); + void SSU2Session::SendSessionRequest(uint64_t token) { + // we are Alice + m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair(); + m_SentHandshakePacket.reset(new HandshakePacket); + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + m_SentHandshakePacket->sendTime = ts; - if (m_TerminationReason == eSSU2TerminationReasonNormalClose) - { - m_Server.AddSession (shared_from_this ()); - SendSessionCreated (headerX + 16); - } - else - SendRetry (); - } + Header &header = m_SentHandshakePacket->header; + uint8_t *headerX = m_SentHandshakePacket->headerX, + *payload = m_SentHandshakePacket->payload; + // fill packet + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionRequest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t) i2p::context.GetNetID(); // netID + header.h.flags[2] = 0; // flag + memcpy(headerX, &m_SourceConnID, 8); // source id + memcpy(headerX + 8, &token, 8); // token + memcpy(headerX + 16, m_EphemeralKeys->GetPublicKey(), 32); // X + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, ts / 1000); + size_t payloadSize = 7; + if (GetRouterStatus() == eRouterStatusFirewalled && m_Address->IsIntroducer()) { + // relay tag request + payload[payloadSize] = eSSU2BlkRelayTagRequest; + memset(payload + payloadSize + 1, 0, 2); // size = 0 + payloadSize += 3; + } + payloadSize += CreatePaddingBlock(payload + payloadSize, 40 - payloadSize, 1); + // KDF for session request + m_NoiseState->MixHash({{header.buf, 16}, + {headerX, 16}}); // h = SHA256(h || header) + m_NoiseState->MixHash(m_EphemeralKeys->GetPublicKey(), 32); // h = SHA256(h || aepk); + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree(m_Address->s, sharedSecret); + m_NoiseState->MixKey(sharedSecret); + // encrypt + const uint8_t nonce[12] = {0}; + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, + nonce, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask(m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(m_Address->i, payload + (payloadSize - 12)); + i2p::crypto::ChaCha20(headerX, 48, m_Address->i, nonce, headerX); + m_NoiseState->MixHash(payload, + payloadSize); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated + m_SentHandshakePacket->payloadSize = payloadSize; + // send + if (m_State == eSSU2SessionStateTokenReceived || m_Server.AddPendingOutgoingSession(shared_from_this())) { + m_State = eSSU2SessionStateSessionRequestSent; + m_Server.Send(header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); + } else { + LogPrint(eLogWarning, "SSU2: SessionRequest request to ", m_RemoteEndpoint, " already pending"); + Terminate(); + } + } - void SSU2Session::SendSessionCreated (const uint8_t * X) - { - // we are Bob - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - m_SentHandshakePacket.reset (new HandshakePacket); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - m_SentHandshakePacket->sendTime = ts; - - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) - // fill packet - Header& header = m_SentHandshakePacket->header; - uint8_t * headerX = m_SentHandshakePacket->headerX, - * payload = m_SentHandshakePacket->payload; - header.h.connID = m_DestConnID; // dest id - header.h.packetNum = 0; - header.h.type = eSSU2SessionCreated; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (headerX, &m_SourceConnID, 8); // source id - memset (headerX + 8, 0, 8); // token = 0 - memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // Y - // payload - size_t maxPayloadSize = m_MaxPayloadSize - 48; - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, ts/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, maxPayloadSize - payloadSize, m_RemoteEndpoint); - if (m_RelayTag) - { - payload[payloadSize] = eSSU2BlkRelayTag; - htobe16buf (payload + payloadSize + 1, 4); - htobe32buf (payload + payloadSize + 3, m_RelayTag); - payloadSize += 7; - } - auto token = m_Server.NewIncomingToken (m_RemoteEndpoint); - if (ts + SSU2_TOKEN_EXPIRATION_THRESHOLD > token.second) // not expired? - { - payload[payloadSize] = eSSU2BlkNewToken; - htobe16buf (payload + payloadSize + 1, 12); - htobe32buf (payload + payloadSize + 3, token.second - SSU2_TOKEN_EXPIRATION_THRESHOLD); // expires - memcpy (payload + payloadSize + 7, &token.first, 8); // token - payloadSize += 15; - } - payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); - // KDF for SessionCreated - m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) - m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (X, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // encrypt - const uint8_t nonce[12] = {0}; - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted Noise payload from Session Created) - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); - i2p::crypto::ChaCha20 (headerX, 48, kh2, nonce, headerX); - m_State = eSSU2SessionStateSessionCreatedSent; - m_SentHandshakePacket->payloadSize = payloadSize; - // send - m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); - } + void SSU2Session::ProcessSessionRequest(Header &header, uint8_t *buf, size_t len) { + // we are Bob + const uint8_t nonce[12] = {0}; + uint8_t headerX[48]; + i2p::crypto::ChaCha20(buf + 16, 48, i2p::context.GetSSU2IntroKey(), nonce, headerX); + memcpy(&m_DestConnID, headerX, 8); + uint64_t token; + memcpy(&token, headerX + 8, 8); + if (!token || token != m_Server.GetIncomingToken(m_RemoteEndpoint)) { + LogPrint(eLogDebug, "SSU2: SessionRequest token mismatch. Retry"); + SendRetry(); + return; + } + // KDF for session request + m_NoiseState->MixHash({{header.buf, 16}, + {headerX, 16}}); // h = SHA256(h || header) + m_NoiseState->MixHash(headerX + 16, 32); // h = SHA256(h || aepk); + uint8_t sharedSecret[32]; + i2p::context.GetSSU2StaticKeys().Agree(headerX + 16, sharedSecret); + m_NoiseState->MixKey(sharedSecret); + // decrypt + uint8_t *payload = buf + 64; + std::vector decryptedPayload(len - 80); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 80, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, decryptedPayload.data(), + decryptedPayload.size(), false)) { + LogPrint(eLogWarning, "SSU2: SessionRequest AEAD verification failed "); + return; + } + m_NoiseState->MixHash(payload, len - + 64); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated + // payload + m_State = eSSU2SessionStateSessionRequestReceived; + HandlePayload(decryptedPayload.data(), decryptedPayload.size()); - bool SSU2Session::ProcessSessionCreated (uint8_t * buf, size_t len) - { - // we are Alice - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (m_Address->i, buf + (len - 24)); - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) - header.ll[1] ^= CreateHeaderMask (kh2, buf + (len - 12)); - if (header.h.type != eSSU2SessionCreated) - // this situation is valid, because it might be Retry with different encryption - return false; - const uint8_t nonce[12] = {0}; - uint8_t headerX[48]; - i2p::crypto::ChaCha20 (buf + 16, 48, kh2, nonce, headerX); - // KDF for SessionCreated - m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) - m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (headerX + 16, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // decrypt - uint8_t * payload = buf + 64; - std::vector decryptedPayload(len - 80); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) - { - LogPrint (eLogWarning, "SSU2: SessionCreated AEAD verification failed "); - return false; - } - m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from SessionCreated) for SessionConfirmed - // payload - m_State = eSSU2SessionStateSessionCreatedReceived; - HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); + if (m_TerminationReason == eSSU2TerminationReasonNormalClose) { + m_Server.AddSession(shared_from_this()); + SendSessionCreated(headerX + 16); + } else + SendRetry(); + } - m_Server.AddSession (shared_from_this ()); - AdjustMaxPayloadSize (); - SendSessionConfirmed (headerX + 16); - KDFDataPhase (m_KeyDataSend, m_KeyDataReceive); + void SSU2Session::SendSessionCreated(const uint8_t *X) { + // we are Bob + m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair(); + m_SentHandshakePacket.reset(new HandshakePacket); + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + m_SentHandshakePacket->sendTime = ts; - return true; - } + uint8_t kh2[32]; + i2p::crypto::HKDF(m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, + 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) + // fill packet + Header &header = m_SentHandshakePacket->header; + uint8_t *headerX = m_SentHandshakePacket->headerX, + *payload = m_SentHandshakePacket->payload; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionCreated; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t) i2p::context.GetNetID(); // netID + header.h.flags[2] = 0; // flag + memcpy(headerX, &m_SourceConnID, 8); // source id + memset(headerX + 8, 0, 8); // token = 0 + memcpy(headerX + 16, m_EphemeralKeys->GetPublicKey(), 32); // Y + // payload + size_t maxPayloadSize = m_MaxPayloadSize - 48; + payload[0] = eSSU2BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, ts / 1000); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock(payload + payloadSize, maxPayloadSize - payloadSize, m_RemoteEndpoint); + if (m_RelayTag) { + payload[payloadSize] = eSSU2BlkRelayTag; + htobe16buf(payload + payloadSize + 1, 4); + htobe32buf(payload + payloadSize + 3, m_RelayTag); + payloadSize += 7; + } + auto token = m_Server.NewIncomingToken(m_RemoteEndpoint); + if (ts + SSU2_TOKEN_EXPIRATION_THRESHOLD > token.second) // not expired? + { + payload[payloadSize] = eSSU2BlkNewToken; + htobe16buf(payload + payloadSize + 1, 12); + htobe32buf(payload + payloadSize + 3, token.second - SSU2_TOKEN_EXPIRATION_THRESHOLD); // expires + memcpy(payload + payloadSize + 7, &token.first, 8); // token + payloadSize += 15; + } + payloadSize += CreatePaddingBlock(payload + payloadSize, maxPayloadSize - payloadSize); + // KDF for SessionCreated + m_NoiseState->MixHash({{header.buf, 16}, + {headerX, 16}}); // h = SHA256(h || header) + m_NoiseState->MixHash(headerX + 16, 32); // h = SHA256(h || bepk); + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree(X, sharedSecret); + m_NoiseState->MixKey(sharedSecret); + // encrypt + const uint8_t nonce[12] = {0}; + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, + nonce, payload, payloadSize + 16, true); + payloadSize += 16; + m_NoiseState->MixHash(payload, + payloadSize); // h = SHA256(h || encrypted Noise payload from Session Created) + header.ll[0] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(kh2, payload + (payloadSize - 12)); + i2p::crypto::ChaCha20(headerX, 48, kh2, nonce, headerX); + m_State = eSSU2SessionStateSessionCreatedSent; + m_SentHandshakePacket->payloadSize = payloadSize; + // send + m_Server.Send(header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); + } - void SSU2Session::SendSessionConfirmed (const uint8_t * Y) - { - // we are Alice - m_SentHandshakePacket.reset (new HandshakePacket); - m_SentHandshakePacket->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) - // fill packet - Header& header = m_SentHandshakePacket->header; - header.h.connID = m_DestConnID; // dest id - header.h.packetNum = 0; - header.h.type = eSSU2SessionConfirmed; - memset (header.h.flags, 0, 3); - header.h.flags[0] = 1; // frag, total fragments always 1 - // payload - size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1 - uint8_t * payload = m_SentHandshakePacket->payload; - size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); - if (!payloadSize) - { - // split by two fragments - maxPayloadSize += m_MaxPayloadSize; - payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); - header.h.flags[0] = 0x02; // frag 0, total fragments 2 - // TODO: check if we need more fragments - } - if (payloadSize < maxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); - // KDF for Session Confirmed part 1 - m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) - // Encrypt part 1 - uint8_t * part1 = m_SentHandshakePacket->headerX; - uint8_t nonce[12]; - CreateNonce (1, nonce); - i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetSSU2StaticPublicKey (), 32, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, part1, 48, true); - m_NoiseState->MixHash (part1, 48); // h = SHA256(h || ciphertext); - // KDF for Session Confirmed part 2 - uint8_t sharedSecret[32]; - i2p::context.GetSSU2StaticKeys ().Agree (Y, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // Encrypt part2 - memset (nonce, 0, 12); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || ciphertext); - m_SentHandshakePacket->payloadSize = payloadSize; - if (header.h.flags[0] > 1) - { - if (payloadSize > m_MaxPayloadSize - 48) - { - payloadSize = m_MaxPayloadSize - 48 - (rand () % 16); - if (m_SentHandshakePacket->payloadSize - payloadSize < 24) - payloadSize -= 24; - } - else - header.h.flags[0] = 1; - } - // Encrypt header - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); - m_State = eSSU2SessionStateSessionConfirmedSent; - // send - m_Server.Send (header.buf, 16, part1, 48, payload, payloadSize, m_RemoteEndpoint); - m_SendPacketNum++; - if (m_SentHandshakePacket->payloadSize > payloadSize) - { - // send second fragment - m_SessionConfirmedFragment.reset (new HandshakePacket); - Header& header = m_SessionConfirmedFragment->header; - header.h.connID = m_DestConnID; // dest id - header.h.packetNum = 0; - header.h.type = eSSU2SessionConfirmed; - memset (header.h.flags, 0, 3); - header.h.flags[0] = 0x12; // frag 1, total fragments 2 - m_SessionConfirmedFragment->payloadSize = m_SentHandshakePacket->payloadSize - payloadSize; - memcpy (m_SessionConfirmedFragment->payload, m_SentHandshakePacket->payload + payloadSize, m_SessionConfirmedFragment->payloadSize); - m_SentHandshakePacket->payloadSize = payloadSize; - header.ll[0] ^= CreateHeaderMask (m_Address->i, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (kh2, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 12)); - m_Server.Send (header.buf, 16, m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); - } - } + bool SSU2Session::ProcessSessionCreated(uint8_t *buf, size_t len) { + // we are Alice + Header header; + memcpy(header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask(m_Address->i, buf + (len - 24)); + uint8_t kh2[32]; + i2p::crypto::HKDF(m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, + 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) + header.ll[1] ^= CreateHeaderMask(kh2, buf + (len - 12)); + if (header.h.type != eSSU2SessionCreated) + // this situation is valid, because it might be Retry with different encryption + return false; + const uint8_t nonce[12] = {0}; + uint8_t headerX[48]; + i2p::crypto::ChaCha20(buf + 16, 48, kh2, nonce, headerX); + // KDF for SessionCreated + m_NoiseState->MixHash({{header.buf, 16}, + {headerX, 16}}); // h = SHA256(h || header) + m_NoiseState->MixHash(headerX + 16, 32); // h = SHA256(h || bepk); + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree(headerX + 16, sharedSecret); + m_NoiseState->MixKey(sharedSecret); + // decrypt + uint8_t *payload = buf + 64; + std::vector decryptedPayload(len - 80); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 80, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, decryptedPayload.data(), + decryptedPayload.size(), false)) { + LogPrint(eLogWarning, "SSU2: SessionCreated AEAD verification failed "); + return false; + } + m_NoiseState->MixHash(payload, len - + 64); // h = SHA256(h || encrypted payload from SessionCreated) for SessionConfirmed + // payload + m_State = eSSU2SessionStateSessionCreatedReceived; + HandlePayload(decryptedPayload.data(), decryptedPayload.size()); - bool SSU2Session::ProcessSessionConfirmed (uint8_t * buf, size_t len) - { - // we are Bob - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) - header.ll[1] ^= CreateHeaderMask (kh2, buf + (len - 12)); - if (header.h.type != eSSU2SessionConfirmed) - { - LogPrint (eLogInfo, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2SessionConfirmed); - // TODO: queue up - return true; - } - // check if fragmented - if ((header.h.flags[0] & 0x0F) > 1) - { - // fragmented - if (!(header.h.flags[0] & 0xF0)) - { - // first fragment - if (!m_SessionConfirmedFragment) - { - m_SessionConfirmedFragment.reset (new HandshakePacket); - m_SessionConfirmedFragment->header = header; - memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize = len - 16; - return true; // wait for second fragment - } - else if (m_SessionConfirmedFragment->isSecondFragment) - { - // we have second fragment - m_SessionConfirmedFragment->header = header; - memmove (m_SessionConfirmedFragment->payload + (len - 16), m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize); - memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize += (len - 16); - buf = m_SessionConfirmedFragment->payload - 16; - len = m_SessionConfirmedFragment->payloadSize + 16; - } - else - return true; - } - else - { - // second fragment - if (!m_SessionConfirmedFragment) - { - // out of sequence, save it - m_SessionConfirmedFragment.reset (new HandshakePacket); - memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize = len - 16; - m_SessionConfirmedFragment->isSecondFragment = true; - return true; - } - header = m_SessionConfirmedFragment->header; - memcpy (m_SessionConfirmedFragment->payload + m_SessionConfirmedFragment->payloadSize, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize += (len - 16); - buf = m_SessionConfirmedFragment->payload - 16; - len = m_SessionConfirmedFragment->payloadSize + 16; - } - } - // KDF for Session Confirmed part 1 - m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) - // decrypt part1 - uint8_t nonce[12]; - CreateNonce (1, nonce); - uint8_t S[32]; - if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, 32, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, S, 32, false)) - { - LogPrint (eLogWarning, "SSU2: SessionConfirmed part 1 AEAD verification failed "); - return false; - } - m_NoiseState->MixHash (buf + 16, 48); // h = SHA256(h || ciphertext); - // KDF for Session Confirmed part 2 and data phase - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (S, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - KDFDataPhase (m_KeyDataReceive, m_KeyDataSend); - // decrypt part2 - memset (nonce, 0, 12); - uint8_t * payload = buf + 64; - std::vector decryptedPayload(len - 80); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) - { - LogPrint (eLogWarning, "SSU2: SessionConfirmed part 2 AEAD verification failed "); - return false; - } - m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || ciphertext); - // payload - // handle RouterInfo block that must be first - if (decryptedPayload[0] != eSSU2BlkRouterInfo) - { - LogPrint (eLogError, "SSU2: SessionConfirmed unexpected first block type ", (int)decryptedPayload[0]); - return false; - } - size_t riSize = bufbe16toh (decryptedPayload.data () + 1); - if (riSize + 3 > decryptedPayload.size ()) - { - LogPrint (eLogError, "SSU2: SessionConfirmed RouterInfo block is too long ", riSize); - return false; - } - LogPrint (eLogDebug, "SSU2: RouterInfo in SessionConfirmed"); - auto ri = ExtractRouterInfo (decryptedPayload.data () + 3, riSize); - if (!ri) - { - LogPrint (eLogError, "SSU2: SessionConfirmed malformed RouterInfo block"); - return false; - } - m_Address = ri->GetSSU2AddressWithStaticKey (S, m_RemoteEndpoint.address ().is_v6 ()); - if (!m_Address) - { - LogPrint (eLogError, "SSU2: No SSU2 address with static key found in SessionConfirmed from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); - return false; - } - // update RouterInfo in netdb - ri = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // ri points to one from netdb now - if (!ri) - { - LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb"); - return false; - } - SetRemoteIdentity (ri->GetRouterIdentity ()); - AdjustMaxPayloadSize (); - m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now - m_RemoteTransports = ri->GetCompatibleTransports (false); - // handle other blocks - HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); - Established (); + m_Server.AddSession(shared_from_this()); + AdjustMaxPayloadSize(); + SendSessionConfirmed(headerX + 16); + KDFDataPhase(m_KeyDataSend, m_KeyDataReceive); - SendQuickAck (); + return true; + } - return true; - } + void SSU2Session::SendSessionConfirmed(const uint8_t *Y) { + // we are Alice + m_SentHandshakePacket.reset(new HandshakePacket); + m_SentHandshakePacket->sendTime = i2p::util::GetMillisecondsSinceEpoch(); - void SSU2Session::KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba) - { - uint8_t keydata[64]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) - // ab - i2p::crypto::HKDF (keydata, nullptr, 0, "HKDFSSU2DataKeys", keydata_ab); // keydata_ab = HKDF(keydata, ZEROLEN, "HKDFSSU2DataKeys", 64) - // ba - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "HKDFSSU2DataKeys", keydata_ba); // keydata_ba = HKDF(keydata + 32, ZEROLEN, "HKDFSSU2DataKeys", 64) - } + uint8_t kh2[32]; + i2p::crypto::HKDF(m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, + 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) + // fill packet + Header &header = m_SentHandshakePacket->header; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionConfirmed; + memset(header.h.flags, 0, 3); + header.h.flags[0] = 1; // frag, total fragments always 1 + // payload + size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1 + uint8_t *payload = m_SentHandshakePacket->payload; + size_t payloadSize = CreateRouterInfoBlock(payload, maxPayloadSize, i2p::context.GetSharedRouterInfo()); + if (!payloadSize) { + // split by two fragments + maxPayloadSize += m_MaxPayloadSize; + payloadSize = CreateRouterInfoBlock(payload, maxPayloadSize, i2p::context.GetSharedRouterInfo()); + header.h.flags[0] = 0x02; // frag 0, total fragments 2 + // TODO: check if we need more fragments + } + if (payloadSize < maxPayloadSize) + payloadSize += CreatePaddingBlock(payload + payloadSize, maxPayloadSize - payloadSize); + // KDF for Session Confirmed part 1 + m_NoiseState->MixHash(header.buf, 16); // h = SHA256(h || header) + // Encrypt part 1 + uint8_t *part1 = m_SentHandshakePacket->headerX; + uint8_t nonce[12]; + CreateNonce(1, nonce); + i2p::crypto::AEADChaCha20Poly1305(i2p::context.GetSSU2StaticPublicKey(), 32, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, part1, 48, true); + m_NoiseState->MixHash(part1, 48); // h = SHA256(h || ciphertext); + // KDF for Session Confirmed part 2 + uint8_t sharedSecret[32]; + i2p::context.GetSSU2StaticKeys().Agree(Y, sharedSecret); + m_NoiseState->MixKey(sharedSecret); + // Encrypt part2 + memset(nonce, 0, 12); + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, + nonce, payload, payloadSize + 16, true); + payloadSize += 16; + m_NoiseState->MixHash(payload, payloadSize); // h = SHA256(h || ciphertext); + m_SentHandshakePacket->payloadSize = payloadSize; + if (header.h.flags[0] > 1) { + if (payloadSize > m_MaxPayloadSize - 48) { + payloadSize = m_MaxPayloadSize - 48 - (rand() % 16); + if (m_SentHandshakePacket->payloadSize - payloadSize < 24) + payloadSize -= 24; + } else + header.h.flags[0] = 1; + } + // Encrypt header + header.ll[0] ^= CreateHeaderMask(m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(kh2, payload + (payloadSize - 12)); + m_State = eSSU2SessionStateSessionConfirmedSent; + // send + m_Server.Send(header.buf, 16, part1, 48, payload, payloadSize, m_RemoteEndpoint); + m_SendPacketNum++; + if (m_SentHandshakePacket->payloadSize > payloadSize) { + // send second fragment + m_SessionConfirmedFragment.reset(new HandshakePacket); + Header &header = m_SessionConfirmedFragment->header; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionConfirmed; + memset(header.h.flags, 0, 3); + header.h.flags[0] = 0x12; // frag 1, total fragments 2 + m_SessionConfirmedFragment->payloadSize = m_SentHandshakePacket->payloadSize - payloadSize; + memcpy(m_SessionConfirmedFragment->payload, m_SentHandshakePacket->payload + payloadSize, + m_SessionConfirmedFragment->payloadSize); + m_SentHandshakePacket->payloadSize = payloadSize; + header.ll[0] ^= CreateHeaderMask(m_Address->i, m_SessionConfirmedFragment->payload + + (m_SessionConfirmedFragment->payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(kh2, m_SessionConfirmedFragment->payload + + (m_SessionConfirmedFragment->payloadSize - 12)); + m_Server.Send(header.buf, 16, m_SessionConfirmedFragment->payload, + m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); + } + } - void SSU2Session::SendTokenRequest () - { - // we are Alice - Header header; - uint8_t h[32], payload[41]; - // fill packet - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2TokenRequest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - memcpy (h + 16, &m_SourceConnID, 8); // source id - memset (h + 24, 0, 8); // zero token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); - size_t payloadSize = 7; - payloadSize += CreatePaddingBlock (payload + payloadSize, 25 - payloadSize, 1); - // encrypt - uint8_t nonce[12]; - CreateNonce (be32toh (header.h.packetNum), nonce); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, m_Address->i, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); - memset (nonce, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, m_Address->i, nonce, h + 16); - // send - if (m_Server.AddPendingOutgoingSession (shared_from_this ())) - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); - else - { - LogPrint (eLogWarning, "SSU2: TokenRequest request to ", m_RemoteEndpoint, " already pending"); - Terminate (); - } - } + bool SSU2Session::ProcessSessionConfirmed(uint8_t *buf, size_t len) { + // we are Bob + Header header; + memcpy(header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 24)); + uint8_t kh2[32]; + i2p::crypto::HKDF(m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, + 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) + header.ll[1] ^= CreateHeaderMask(kh2, buf + (len - 12)); + if (header.h.type != eSSU2SessionConfirmed) { + LogPrint(eLogInfo, "SSU2: Unexpected message type ", (int) header.h.type, " instead ", + (int) eSSU2SessionConfirmed); + // TODO: queue up + return true; + } + // check if fragmented + if ((header.h.flags[0] & 0x0F) > 1) { + // fragmented + if (!(header.h.flags[0] & 0xF0)) { + // first fragment + if (!m_SessionConfirmedFragment) { + m_SessionConfirmedFragment.reset(new HandshakePacket); + m_SessionConfirmedFragment->header = header; + memcpy(m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize = len - 16; + return true; // wait for second fragment + } else if (m_SessionConfirmedFragment->isSecondFragment) { + // we have second fragment + m_SessionConfirmedFragment->header = header; + memmove(m_SessionConfirmedFragment->payload + (len - 16), m_SessionConfirmedFragment->payload, + m_SessionConfirmedFragment->payloadSize); + memcpy(m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize += (len - 16); + buf = m_SessionConfirmedFragment->payload - 16; + len = m_SessionConfirmedFragment->payloadSize + 16; + } else + return true; + } else { + // second fragment + if (!m_SessionConfirmedFragment) { + // out of sequence, save it + m_SessionConfirmedFragment.reset(new HandshakePacket); + memcpy(m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize = len - 16; + m_SessionConfirmedFragment->isSecondFragment = true; + return true; + } + header = m_SessionConfirmedFragment->header; + memcpy(m_SessionConfirmedFragment->payload + m_SessionConfirmedFragment->payloadSize, buf + 16, + len - 16); + m_SessionConfirmedFragment->payloadSize += (len - 16); + buf = m_SessionConfirmedFragment->payload - 16; + len = m_SessionConfirmedFragment->payloadSize + 16; + } + } + // KDF for Session Confirmed part 1 + m_NoiseState->MixHash(header.buf, 16); // h = SHA256(h || header) + // decrypt part1 + uint8_t nonce[12]; + CreateNonce(1, nonce); + uint8_t S[32]; + if (!i2p::crypto::AEADChaCha20Poly1305(buf + 16, 32, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, S, 32, false)) { + LogPrint(eLogWarning, "SSU2: SessionConfirmed part 1 AEAD verification failed "); + return false; + } + m_NoiseState->MixHash(buf + 16, 48); // h = SHA256(h || ciphertext); + // KDF for Session Confirmed part 2 and data phase + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree(S, sharedSecret); + m_NoiseState->MixKey(sharedSecret); + KDFDataPhase(m_KeyDataReceive, m_KeyDataSend); + // decrypt part2 + memset(nonce, 0, 12); + uint8_t *payload = buf + 64; + std::vector decryptedPayload(len - 80); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 80, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, decryptedPayload.data(), + decryptedPayload.size(), false)) { + LogPrint(eLogWarning, "SSU2: SessionConfirmed part 2 AEAD verification failed "); + return false; + } + m_NoiseState->MixHash(payload, len - 64); // h = SHA256(h || ciphertext); + // payload + // handle RouterInfo block that must be first + if (decryptedPayload[0] != eSSU2BlkRouterInfo) { + LogPrint(eLogError, "SSU2: SessionConfirmed unexpected first block type ", (int) decryptedPayload[0]); + return false; + } + size_t riSize = bufbe16toh(decryptedPayload.data() + 1); + if (riSize + 3 > decryptedPayload.size()) { + LogPrint(eLogError, "SSU2: SessionConfirmed RouterInfo block is too long ", riSize); + return false; + } + LogPrint(eLogDebug, "SSU2: RouterInfo in SessionConfirmed"); + auto ri = ExtractRouterInfo(decryptedPayload.data() + 3, riSize); + if (!ri) { + LogPrint(eLogError, "SSU2: SessionConfirmed malformed RouterInfo block"); + return false; + } + m_Address = ri->GetSSU2AddressWithStaticKey(S, m_RemoteEndpoint.address().is_v6()); + if (!m_Address) { + LogPrint(eLogError, "SSU2: No SSU2 address with static key found in SessionConfirmed from ", + i2p::data::GetIdentHashAbbreviation(ri->GetIdentHash())); + return false; + } + // update RouterInfo in netdb + ri = i2p::data::netdb.AddRouterInfo(ri->GetBuffer(), ri->GetBufferLen()); // ri points to one from netdb now + if (!ri) { + LogPrint(eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb"); + return false; + } + SetRemoteIdentity(ri->GetRouterIdentity()); + AdjustMaxPayloadSize(); + m_Server.AddSessionByRouterHash(shared_from_this()); // we know remote router now + m_RemoteTransports = ri->GetCompatibleTransports(false); + // handle other blocks + HandlePayload(decryptedPayload.data() + riSize + 3, decryptedPayload.size() - riSize - 3); + Established(); - void SSU2Session::ProcessTokenRequest (Header& header, uint8_t * buf, size_t len) - { - // we are Bob - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: Incorrect TokenRequest len ", len); - return; - } - uint8_t nonce[12] = {0}; - uint8_t h[32]; - memcpy (h, header.buf, 16); - i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); - memcpy (&m_DestConnID, h + 16, 8); - // decrypt - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t * payload = buf + 32; - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: TokenRequest AEAD verification failed "); - return; - } - // payload - m_State = eSSU2SessionStateTokenRequestReceived; - HandlePayload (payload, len - 48); - SendRetry (); - } + SendQuickAck(); - void SSU2Session::SendRetry () - { - // we are Bob - Header header; - uint8_t h[32], payload[72]; - // fill packet - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2Retry; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - memcpy (h + 16, &m_SourceConnID, 8); // source id - uint64_t token = 0; - if (m_TerminationReason == eSSU2TerminationReasonNormalClose) - token = m_Server.GetIncomingToken (m_RemoteEndpoint); - memcpy (h + 24, &token, 8); // token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, 56 - payloadSize, m_RemoteEndpoint); - if (m_TerminationReason != eSSU2TerminationReasonNormalClose) - payloadSize += CreateTerminationBlock (payload + payloadSize, 56 - payloadSize); - payloadSize += CreatePaddingBlock (payload + payloadSize, 56 - payloadSize); - // encrypt - uint8_t nonce[12]; - CreateNonce (be32toh (header.h.packetNum), nonce); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, i2p::context.GetSSU2IntroKey (), nonce, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 12)); - memset (nonce, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); - // send - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); - } + return true; + } - bool SSU2Session::ProcessRetry (uint8_t * buf, size_t len) - { - // we are Alice - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (m_Address->i, buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (m_Address->i, buf + (len - 12)); - if (header.h.type != eSSU2Retry) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Retry); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - i2p::crypto::ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); - uint64_t token = headerX[1]; - if (token) - m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - m_Address->i, nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: Retry AEAD verification failed"); - return false; - } - m_State = eSSU2SessionStateTokenReceived; - HandlePayload (payload, len - 48); - if (!token) - { - // we should handle payload even for zero token to handle Datetime block and adjust clock in case of clock skew - LogPrint (eLogWarning, "SSU2: Retry token is zero"); - return false; - } - InitNoiseXKState1 (*m_NoiseState, m_Address->s); // reset Noise TODO: check state - SendSessionRequest (token); - return true; - } + void SSU2Session::KDFDataPhase(uint8_t *keydata_ab, uint8_t *keydata_ba) { + uint8_t keydata[64]; + i2p::crypto::HKDF(m_NoiseState->m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // ab + i2p::crypto::HKDF(keydata, nullptr, 0, "HKDFSSU2DataKeys", + keydata_ab); // keydata_ab = HKDF(keydata, ZEROLEN, "HKDFSSU2DataKeys", 64) + // ba + i2p::crypto::HKDF(keydata + 32, nullptr, 0, "HKDFSSU2DataKeys", + keydata_ba); // keydata_ba = HKDF(keydata + 32, ZEROLEN, "HKDFSSU2DataKeys", 64) + } - void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, - const uint8_t * introKey, uint64_t token) - { - // we are Charlie - LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2HolePunch; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - uint64_t c = ~header.h.connID; - memcpy (h + 16, &c, 8); // source id - RAND_bytes (h + 24, 8); // token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); - payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, - eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); - memset (n, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); - // send - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); - } + void SSU2Session::SendTokenRequest() { + // we are Alice + Header header; + uint8_t h[32], payload[41]; + // fill packet + header.h.connID = m_DestConnID; // dest id + RAND_bytes(header.buf + 8, 4); // random packet num + header.h.type = eSSU2TokenRequest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t) i2p::context.GetNetID(); // netID + header.h.flags[2] = 0; // flag + memcpy(h, header.buf, 16); + memcpy(h + 16, &m_SourceConnID, 8); // source id + memset(h + 24, 0, 8); // zero token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, i2p::util::GetSecondsSinceEpoch()); + size_t payloadSize = 7; + payloadSize += CreatePaddingBlock(payload + payloadSize, 25 - payloadSize, 1); + // encrypt + uint8_t nonce[12]; + CreateNonce(be32toh(header.h.packetNum), nonce); + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, h, 32, m_Address->i, nonce, payload, + payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask(m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(m_Address->i, payload + (payloadSize - 12)); + memset(nonce, 0, 12); + i2p::crypto::ChaCha20(h + 16, 16, m_Address->i, nonce, h + 16); + // send + if (m_Server.AddPendingOutgoingSession(shared_from_this())) + m_Server.Send(header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + else { + LogPrint(eLogWarning, "SSU2: TokenRequest request to ", m_RemoteEndpoint, " already pending"); + Terminate(); + } + } - bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) - { - // we are Alice - LogPrint (eLogDebug, "SSU2: HolePunch"); - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2HolePunch) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2HolePunch); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - m_DestConnID = headerX[0]; - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: HolePunch AEAD verification failed "); - return false; - } - HandlePayload (payload, len - 48); - // connect to Charlie - ConnectAfterIntroduction (); + void SSU2Session::ProcessTokenRequest(Header &header, uint8_t *buf, size_t len) { + // we are Bob + if (len < 48) { + LogPrint(eLogWarning, "SSU2: Incorrect TokenRequest len ", len); + return; + } + uint8_t nonce[12] = {0}; + uint8_t h[32]; + memcpy(h, header.buf, 16); + i2p::crypto::ChaCha20(buf + 16, 16, i2p::context.GetSSU2IntroKey(), nonce, h + 16); + memcpy(&m_DestConnID, h + 16, 8); + // decrypt + CreateNonce(be32toh(header.h.packetNum), nonce); + uint8_t *payload = buf + 32; + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey(), nonce, payload, len - 48, false)) { + LogPrint(eLogWarning, "SSU2: TokenRequest AEAD verification failed "); + return; + } + // payload + m_State = eSSU2SessionStateTokenRequestReceived; + HandlePayload(payload, len - 48); + SendRetry(); + } - return true; - } + void SSU2Session::SendRetry() { + // we are Bob + Header header; + uint8_t h[32], payload[72]; + // fill packet + header.h.connID = m_DestConnID; // dest id + RAND_bytes(header.buf + 8, 4); // random packet num + header.h.type = eSSU2Retry; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t) i2p::context.GetNetID(); // netID + header.h.flags[2] = 0; // flag + memcpy(h, header.buf, 16); + memcpy(h + 16, &m_SourceConnID, 8); // source id + uint64_t token = 0; + if (m_TerminationReason == eSSU2TerminationReasonNormalClose) + token = m_Server.GetIncomingToken(m_RemoteEndpoint); + memcpy(h + 24, &token, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, i2p::util::GetSecondsSinceEpoch()); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock(payload + payloadSize, 56 - payloadSize, m_RemoteEndpoint); + if (m_TerminationReason != eSSU2TerminationReasonNormalClose) + payloadSize += CreateTerminationBlock(payload + payloadSize, 56 - payloadSize); + payloadSize += CreatePaddingBlock(payload + payloadSize, 56 - payloadSize); + // encrypt + uint8_t nonce[12]; + CreateNonce(be32toh(header.h.packetNum), nonce); + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, h, 32, i2p::context.GetSSU2IntroKey(), nonce, + payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), payload + (payloadSize - 12)); + memset(nonce, 0, 12); + i2p::crypto::ChaCha20(h + 16, 16, i2p::context.GetSSU2IntroKey(), nonce, h + 16); + // send + m_Server.Send(header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + } - void SSU2Session::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey) - { - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2PeerTest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - memcpy (h + 16, &m_SourceConnID, 8); // source id - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); - size_t payloadSize = 7; - if (msg == 6 || msg == 7) - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint); - payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, - msg, eSSU2PeerTestCodeAccept, nullptr, signedData, signedDataLen); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); - memset (n, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); - // send - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); - } - - bool SSU2Session::ProcessPeerTest (uint8_t * buf, size_t len) - { - // we are Alice or Charlie - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2PeerTest) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - m_DestConnID = headerX[0]; - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); - return false; - } - HandlePayload (payload, len - 48); - return true; - } + bool SSU2Session::ProcessRetry(uint8_t *buf, size_t len) { + // we are Alice + Header header; + memcpy(header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask(m_Address->i, buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask(m_Address->i, buf + (len - 12)); + if (header.h.type != eSSU2Retry) { + LogPrint(eLogWarning, "SSU2: Unexpected message type ", (int) header.h.type, " instead ", + (int) eSSU2Retry); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20(buf + 16, 16, m_Address->i, nonce, (uint8_t *) headerX); + uint64_t token = headerX[1]; + if (token) + m_Server.UpdateOutgoingToken(m_RemoteEndpoint, token, + i2p::util::GetSecondsSinceEpoch() + SSU2_TOKEN_EXPIRATION_TIMEOUT); + // decrypt and handle payload + uint8_t *payload = buf + 32; + CreateNonce(be32toh(header.h.packetNum), nonce); + uint8_t h[32]; + memcpy(h, header.buf, 16); + memcpy(h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 48, h, 32, + m_Address->i, nonce, payload, len - 48, false)) { + LogPrint(eLogWarning, "SSU2: Retry AEAD verification failed"); + return false; + } + m_State = eSSU2SessionStateTokenReceived; + HandlePayload(payload, len - 48); + if (!token) { + // we should handle payload even for zero token to handle Datetime block and adjust clock in case of clock skew + LogPrint(eLogWarning, "SSU2: Retry token is zero"); + return false; + } + InitNoiseXKState1(*m_NoiseState, m_Address->s); // reset Noise TODO: check state + SendSessionRequest(token); + return true; + } - uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len) - { - if (len < 8) - { - LogPrint (eLogWarning, "SSU2: Data message payload is too short ", (int)len); - return 0; - } - Header header; - header.h.connID = m_DestConnID; - header.h.packetNum = htobe32 (m_SendPacketNum); - header.h.type = eSSU2Data; - memset (header.h.flags, 0, 3); - uint8_t nonce[12]; - CreateNonce (m_SendPacketNum, nonce); - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - i2p::crypto::AEADChaCha20Poly1305 (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE, true); - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (len - 8)); - header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4)); - m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint); - m_SendPacketNum++; - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumSentBytes += len + 32; - return m_SendPacketNum - 1; - } + void SSU2Session::SendHolePunch(uint32_t nonce, const boost::asio::ip::udp::endpoint &ep, + const uint8_t *introKey, uint64_t token) { + // we are Charlie + LogPrint(eLogDebug, "SSU2: Sending HolePunch to ", ep); + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = htobe64(((uint64_t) nonce << 32) | nonce); // dest id + RAND_bytes(header.buf + 8, 4); // random packet num + header.h.type = eSSU2HolePunch; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t) i2p::context.GetNetID(); // netID + header.h.flags[2] = 0; // flag + memcpy(h, header.buf, 16); + uint64_t c = ~header.h.connID; + memcpy(h + 16, &c, 8); // source id + RAND_bytes(h + 24, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, i2p::util::GetSecondsSinceEpoch()); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); + payloadSize += CreateRelayResponseBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize, + eSSU2RelayResponseCodeAccept, nonce, token, ep.address().is_v4()); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce(be32toh(header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, + true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask(introKey, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(introKey, payload + (payloadSize - 12)); + memset(n, 0, 12); + i2p::crypto::ChaCha20(h + 16, 16, introKey, n, h + 16); + // send + m_Server.Send(header.buf, 16, h + 16, 16, payload, payloadSize, ep); + } - void SSU2Session::ProcessData (uint8_t * buf, size_t len) - { - Header header; - header.ll[0] = m_SourceConnID; - memcpy (header.buf + 8, buf + 8, 8); - header.ll[1] ^= CreateHeaderMask (m_KeyDataReceive + 32, buf + (len - 12)); - if (header.h.type != eSSU2Data) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Data); - if (IsEstablished ()) - SendQuickAck (); // in case it was SessionConfirmed - else - ResendHandshakePacket (); // assume we receive - return; - } - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = len - 32; - uint32_t packetNum = be32toh (header.h.packetNum); - uint8_t nonce[12]; - CreateNonce (packetNum, nonce); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, payloadSize, header.buf, 16, - m_KeyDataReceive, nonce, payload, payloadSize, false)) - { - LogPrint (eLogWarning, "SSU2: Data AEAD verification failed "); - return; - } - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumReceivedBytes += len; - if (!packetNum || UpdateReceivePacketNum (packetNum)) - HandlePayload (payload, payloadSize); - } + bool SSU2Session::ProcessHolePunch(uint8_t *buf, size_t len) { + // we are Alice + LogPrint(eLogDebug, "SSU2: HolePunch"); + Header header; + memcpy(header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 12)); + if (header.h.type != eSSU2HolePunch) { + LogPrint(eLogWarning, "SSU2: Unexpected message type ", (int) header.h.type, " instead ", + (int) eSSU2HolePunch); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20(buf + 16, 16, i2p::context.GetSSU2IntroKey(), nonce, (uint8_t *) headerX); + m_DestConnID = headerX[0]; + // decrypt and handle payload + uint8_t *payload = buf + 32; + CreateNonce(be32toh(header.h.packetNum), nonce); + uint8_t h[32]; + memcpy(h, header.buf, 16); + memcpy(h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey(), nonce, payload, len - 48, false)) { + LogPrint(eLogWarning, "SSU2: HolePunch AEAD verification failed "); + return false; + } + HandlePayload(payload, len - 48); + // connect to Charlie + ConnectAfterIntroduction(); - void SSU2Session::HandlePayload (const uint8_t * buf, size_t len) - { - size_t offset = 0; - while (offset < len) - { - uint8_t blk = buf[offset]; - offset++; - auto size = bufbe16toh (buf + offset); - offset += 2; - LogPrint (eLogDebug, "SSU2: Block type ", (int)blk, " of size ", size); - if (size > len) - { - LogPrint (eLogError, "SSU2: Unexpected block length ", size); - break; - } - switch (blk) - { - case eSSU2BlkDateTime: - LogPrint (eLogDebug, "SSU2: Datetime"); - HandleDateTime (buf + offset, size); - break; - case eSSU2BlkOptions: - LogPrint (eLogDebug, "SSU2: Options"); - break; - case eSSU2BlkRouterInfo: - { - // not from SessionConfirmed, we must add it instantly to use in next block - LogPrint (eLogDebug, "SSU2: RouterInfo"); - auto ri = ExtractRouterInfo (buf + offset, size); - if (ri) - i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri - break; - } - case eSSU2BlkI2NPMessage: - { - LogPrint (eLogDebug, "SSU2: I2NP message"); - auto nextMsg = (buf[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPShortMessage (); - nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header - memcpy (nextMsg->GetNTCP2Header (), buf + offset, size); - nextMsg->FromNTCP2 (); // SSU2 has the same format as NTCP2 - m_Handler.PutNextMessage (std::move (nextMsg)); - m_IsDataReceived = true; - break; - } - case eSSU2BlkFirstFragment: - LogPrint (eLogDebug, "SSU2: First fragment"); - HandleFirstFragment (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkFollowOnFragment: - LogPrint (eLogDebug, "SSU2: Follow-on fragment"); - HandleFollowOnFragment (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkTermination: - LogPrint (eLogDebug, "SSU2: Termination reason=", (int)buf[11]); - if (IsEstablished () && buf[11] != eSSU2TerminationReasonTerminationReceived) - RequestTermination (eSSU2TerminationReasonTerminationReceived); - else - Done (); - break; - case eSSU2BlkRelayRequest: - LogPrint (eLogDebug, "SSU2: RelayRequest"); - HandleRelayRequest (buf + offset, size); - break; - case eSSU2BlkRelayResponse: - LogPrint (eLogDebug, "SSU2: RelayResponse"); - HandleRelayResponse (buf + offset, size); - break; - case eSSU2BlkRelayIntro: - LogPrint (eLogDebug, "SSU2: RelayIntro"); - HandleRelayIntro (buf + offset, size); - break; - case eSSU2BlkPeerTest: - LogPrint (eLogDebug, "SSU2: PeerTest msg=", (int)buf[offset], " code=", (int)buf[offset+1]); - HandlePeerTest (buf + offset, size); - if (buf[offset] < 5) - m_IsDataReceived = true; - break; - case eSSU2BlkNextNonce: - break; - case eSSU2BlkAck: - LogPrint (eLogDebug, "SSU2: Ack"); - HandleAck (buf + offset, size); - break; - case eSSU2BlkAddress: - LogPrint (eLogDebug, "SSU2: Address"); - HandleAddress (buf + offset, size); - break; - case eSSU2BlkIntroKey: - break; - case eSSU2BlkRelayTagRequest: - LogPrint (eLogDebug, "SSU2: RelayTagRequest"); - if (!m_RelayTag) - { - RAND_bytes ((uint8_t *)&m_RelayTag, 4); - m_Server.AddRelay (m_RelayTag, shared_from_this ()); - } - break; - case eSSU2BlkRelayTag: - LogPrint (eLogDebug, "SSU2: RelayTag"); - m_RelayTag = bufbe32toh (buf + offset); - break; - case eSSU2BlkNewToken: - { - LogPrint (eLogDebug, "SSU2: New token"); - uint64_t token; - memcpy (&token, buf + offset + 4, 8); - m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, bufbe32toh (buf + offset)); - break; - } - case eSSU2BlkPathChallenge: - LogPrint (eLogDebug, "SSU2: Path challenge"); - SendPathResponse (buf + offset, size); - break; - case eSSU2BlkPathResponse: - LogPrint (eLogDebug, "SSU2: Path response"); - break; - case eSSU2BlkFirstPacketNumber: - break; - case eSSU2BlkPadding: - LogPrint (eLogDebug, "SSU2: Padding"); - break; - default: - LogPrint (eLogWarning, "SSU2: Unknown block type ", (int)blk); - } - offset += size; - } - } + return true; + } - void SSU2Session::HandleDateTime (const uint8_t * buf, size_t len) - { - int64_t offset = (int64_t)i2p::util::GetSecondsSinceEpoch () - (int64_t)bufbe32toh (buf); - switch (m_State) - { - case eSSU2SessionStateSessionRequestReceived: - case eSSU2SessionStateTokenRequestReceived: - if (std::abs (offset) > SSU2_CLOCK_SKEW) - m_TerminationReason = eSSU2TerminationReasonClockSkew; - break; - case eSSU2SessionStateSessionCreatedReceived: - case eSSU2SessionStateTokenReceived: - if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) || - (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusTesting)) - { - if (m_Server.IsSyncClockFromPeers ()) - { - if (std::abs (offset) > SSU2_CLOCK_THRESHOLD) - { - LogPrint (eLogWarning, "SSU2: Clock adjusted by ", -offset, " seconds"); - i2p::util::AdjustTimeOffset (-offset); - } - } - else if (std::abs (offset) > SSU2_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU2: Clock skew detected ", offset, ". Check your clock"); - i2p::context.SetError (eRouterErrorClockSkew); - } - } - break; - default: ; - }; - } - - void SSU2Session::HandleAck (const uint8_t * buf, size_t len) - { - if (m_State == eSSU2SessionStateSessionConfirmedSent) - { - Established (); - return; - } - if (m_SentPackets.empty ()) return; - if (len < 5) return; - // acnt - uint32_t ackThrough = bufbe32toh (buf); - uint32_t firstPacketNum = ackThrough > buf[4] ? ackThrough - buf[4] : 0; - HandleAckRange (firstPacketNum, ackThrough, i2p::util::GetMillisecondsSinceEpoch ()); // acnt - // ranges - len -= 5; - const uint8_t * ranges = buf + 5; - while (len > 0 && firstPacketNum) - { - uint32_t lastPacketNum = firstPacketNum - 1; - if (*ranges > lastPacketNum) break; - lastPacketNum -= *ranges; ranges++; // nacks - if (*ranges > lastPacketNum + 1) break; - firstPacketNum = lastPacketNum - *ranges + 1; ranges++; // acks - len -= 2; - HandleAckRange (firstPacketNum, lastPacketNum, 0); - } - } + void SSU2Session::SendPeerTest(uint8_t msg, const uint8_t *signedData, size_t signedDataLen, + const uint8_t *introKey) { + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = m_DestConnID; // dest id + RAND_bytes(header.buf + 8, 4); // random packet num + header.h.type = eSSU2PeerTest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t) i2p::context.GetNetID(); // netID + header.h.flags[2] = 0; // flag + memcpy(h, header.buf, 16); + memcpy(h + 16, &m_SourceConnID, 8); // source id + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf(payload + 1, 4); + htobe32buf(payload + 3, i2p::util::GetSecondsSinceEpoch()); + size_t payloadSize = 7; + if (msg == 6 || msg == 7) + payloadSize += CreateAddressBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize, + m_RemoteEndpoint); + payloadSize += CreatePeerTestBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize, + msg, eSSU2PeerTestCodeAccept, nullptr, signedData, signedDataLen); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce(be32toh(header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305(payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, + true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask(introKey, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask(introKey, payload + (payloadSize - 12)); + memset(n, 0, 12); + i2p::crypto::ChaCha20(h + 16, 16, introKey, n, h + 16); + // send + m_Server.Send(header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + } - void SSU2Session::HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts) - { - if (firstPacketNum > lastPacketNum) return; - auto it = m_SentPackets.begin (); - while (it != m_SentPackets.end () && it->first < firstPacketNum) it++; // find first acked packet - if (it == m_SentPackets.end () || it->first > lastPacketNum) return; // not found - auto it1 = it; - int numPackets = 0; - while (it1 != m_SentPackets.end () && it1->first <= lastPacketNum) - { - if (ts && !it1->second->numResends) - { - if (ts > it1->second->sendTime) - { - auto rtt = ts - it1->second->sendTime; - m_RTT = (m_RTT*m_SendPacketNum + rtt)/(m_SendPacketNum + 1); - m_RTO = m_RTT*SSU2_kAPPA; - if (m_RTO < SSU2_MIN_RTO) m_RTO = SSU2_MIN_RTO; - if (m_RTO > SSU2_MAX_RTO) m_RTO = SSU2_MAX_RTO; - } - ts = 0; // update RTT one time per range - } - it1++; - numPackets++; - } - m_SentPackets.erase (it, it1); - if (numPackets > 0) - { - m_WindowSize += numPackets; - if (m_WindowSize > SSU2_MAX_WINDOW_SIZE) m_WindowSize = SSU2_MAX_WINDOW_SIZE; - } - } + bool SSU2Session::ProcessPeerTest(uint8_t *buf, size_t len) { + // we are Alice or Charlie + Header header; + memcpy(header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask(i2p::context.GetSSU2IntroKey(), buf + (len - 12)); + if (header.h.type != eSSU2PeerTest) { + LogPrint(eLogWarning, "SSU2: Unexpected message type ", (int) header.h.type, " instead ", + (int) eSSU2PeerTest); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20(buf + 16, 16, i2p::context.GetSSU2IntroKey(), nonce, (uint8_t *) headerX); + m_DestConnID = headerX[0]; + // decrypt and handle payload + uint8_t *payload = buf + 32; + CreateNonce(be32toh(header.h.packetNum), nonce); + uint8_t h[32]; + memcpy(h, header.buf, 16); + memcpy(h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305(payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey(), nonce, payload, len - 48, false)) { + LogPrint(eLogWarning, "SSU2: PeerTest AEAD verification failed "); + return false; + } + HandlePayload(payload, len - 48); + return true; + } - void SSU2Session::HandleAddress (const uint8_t * buf, size_t len) - { - boost::asio::ip::udp::endpoint ep; - if (ExtractEndpoint (buf, len, ep)) - { - LogPrint (eLogInfo, "SSU2: Our external address is ", ep); - if (!i2p::util::net::IsInReservedRange (ep.address ())) - { - i2p::context.UpdateAddress (ep.address ()); - // check our port - bool isV4 = ep.address ().is_v4 (); - if (ep.port () != m_Server.GetPort (isV4)) - { - if (isV4) - { - if (i2p::context.GetStatus () == eRouterStatusTesting) - i2p::context.SetError (eRouterErrorSymmetricNAT); - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); - } - } - else - { - if (isV4) - { - if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT) - i2p::context.SetStatus (eRouterStatusTesting); - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusError && i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) - i2p::context.SetStatusV6 (eRouterStatusTesting); - } - } - } - } - } - - void SSU2Session::HandleFirstFragment (const uint8_t * buf, size_t len) - { - uint32_t msgID; memcpy (&msgID, buf + 1, 4); - auto msg = NewI2NPShortMessage (); - // same format as I2NP message block - msg->len = msg->offset + len + 7; - memcpy (msg->GetNTCP2Header (), buf, len); - std::shared_ptr m; - bool found = false; - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end ()) - { - found = true; - m = it->second; - } - else - { - m = std::make_shared(); - m_IncompleteMessages.emplace (msgID, m); - } - m->msg = msg; - m->nextFragmentNum = 1; - m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - if (found && ConcatOutOfSequenceFragments (m)) - { - // we have all follow-on fragments already - m->msg->FromNTCP2 (); - m_Handler.PutNextMessage (std::move (m->msg)); - m_IncompleteMessages.erase (it); - } - } + uint32_t SSU2Session::SendData(const uint8_t *buf, size_t len) { + if (len < 8) { + LogPrint(eLogWarning, "SSU2: Data message payload is too short ", (int) len); + return 0; + } + Header header; + header.h.connID = m_DestConnID; + header.h.packetNum = htobe32(m_SendPacketNum); + header.h.type = eSSU2Data; + memset(header.h.flags, 0, 3); + uint8_t nonce[12]; + CreateNonce(m_SendPacketNum, nonce); + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + i2p::crypto::AEADChaCha20Poly1305(buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, + SSU2_MAX_PACKET_SIZE, true); + header.ll[0] ^= CreateHeaderMask(m_Address->i, payload + (len - 8)); + header.ll[1] ^= CreateHeaderMask(m_KeyDataSend + 32, payload + (len + 4)); + m_Server.Send(header.buf, 16, payload, len + 16, m_RemoteEndpoint); + m_SendPacketNum++; + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + m_NumSentBytes += len + 32; + return m_SendPacketNum - 1; + } - void SSU2Session::HandleFollowOnFragment (const uint8_t * buf, size_t len) - { - if (len < 5) return; - uint8_t fragmentNum = buf[0] >> 1; - bool isLast = buf[0] & 0x01; - uint32_t msgID; memcpy (&msgID, buf + 1, 4); - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end ()) - { - if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS && - it->second->msg) - { - // in sequence - it->second->AttachNextFragment (buf + 5, len - 5); - if (isLast) - { - it->second->msg->FromNTCP2 (); - m_Handler.PutNextMessage (std::move (it->second->msg)); - m_IncompleteMessages.erase (it); - } - else - { - if (ConcatOutOfSequenceFragments (it->second)) - { - m_Handler.PutNextMessage (std::move (it->second->msg)); - m_IncompleteMessages.erase (it); - } - else - it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - } - return; - } - } - else - { - // follow-on fragment before first fragment - auto msg = std::make_shared (); - msg->nextFragmentNum = 0; - it = m_IncompleteMessages.emplace (msgID, msg).first; - } - // insert out of sequence fragment - if (fragmentNum >= SSU2_MAX_NUM_FRAGMENTS) - { - LogPrint (eLogWarning, "SSU2: Fragment number ", fragmentNum, " exceeds ", SSU2_MAX_NUM_FRAGMENTS); - return; - } - auto fragment = std::make_shared (); - memcpy (fragment->buf, buf + 5, len -5); - fragment->len = len - 5; - fragment->isLast = isLast; - it->second->outOfSequenceFragments.emplace (fragmentNum, fragment); - it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - } + void SSU2Session::ProcessData(uint8_t *buf, size_t len) { + Header header; + header.ll[0] = m_SourceConnID; + memcpy(header.buf + 8, buf + 8, 8); + header.ll[1] ^= CreateHeaderMask(m_KeyDataReceive + 32, buf + (len - 12)); + if (header.h.type != eSSU2Data) { + LogPrint(eLogWarning, "SSU2: Unexpected message type ", (int) header.h.type, " instead ", + (int) eSSU2Data); + if (IsEstablished()) + SendQuickAck(); // in case it was SessionConfirmed + else + ResendHandshakePacket(); // assume we receive + return; + } + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = len - 32; + uint32_t packetNum = be32toh(header.h.packetNum); + uint8_t nonce[12]; + CreateNonce(packetNum, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305(buf + 16, payloadSize, header.buf, 16, + m_KeyDataReceive, nonce, payload, payloadSize, false)) { + LogPrint(eLogWarning, "SSU2: Data AEAD verification failed "); + return; + } + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + m_NumReceivedBytes += len; + if (!packetNum || UpdateReceivePacketNum(packetNum)) + HandlePayload(payload, payloadSize); + } - bool SSU2Session::ConcatOutOfSequenceFragments (std::shared_ptr m) - { - if (!m) return false; - bool isLast = false; - for (auto it = m->outOfSequenceFragments.begin (); it != m->outOfSequenceFragments.end ();) - if (it->first == m->nextFragmentNum) - { - m->AttachNextFragment (it->second->buf, it->second->len); - isLast = it->second->isLast; - it = m->outOfSequenceFragments.erase (it); - } - else - break; - return isLast; - } + void SSU2Session::HandlePayload(const uint8_t *buf, size_t len) { + size_t offset = 0; + while (offset < len) { + uint8_t blk = buf[offset]; + offset++; + auto size = bufbe16toh(buf + offset); + offset += 2; + LogPrint(eLogDebug, "SSU2: Block type ", (int) blk, " of size ", size); + if (size > len) { + LogPrint(eLogError, "SSU2: Unexpected block length ", size); + break; + } + switch (blk) { + case eSSU2BlkDateTime: + LogPrint(eLogDebug, "SSU2: Datetime"); + HandleDateTime(buf + offset, size); + break; + case eSSU2BlkOptions: + LogPrint(eLogDebug, "SSU2: Options"); + break; + case eSSU2BlkRouterInfo: { + // not from SessionConfirmed, we must add it instantly to use in next block + LogPrint(eLogDebug, "SSU2: RouterInfo"); + auto ri = ExtractRouterInfo(buf + offset, size); + if (ri) + i2p::data::netdb.AddRouterInfo(ri->GetBuffer(), ri->GetBufferLen()); // TODO: add ri + break; + } + case eSSU2BlkI2NPMessage: { + LogPrint(eLogDebug, "SSU2: I2NP message"); + auto nextMsg = (buf[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage(true) + : NewI2NPShortMessage(); + nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header + memcpy(nextMsg->GetNTCP2Header(), buf + offset, size); + nextMsg->FromNTCP2(); // SSU2 has the same format as NTCP2 + m_Handler.PutNextMessage(std::move(nextMsg)); + m_IsDataReceived = true; + break; + } + case eSSU2BlkFirstFragment: + LogPrint(eLogDebug, "SSU2: First fragment"); + HandleFirstFragment(buf + offset, size); + m_IsDataReceived = true; + break; + case eSSU2BlkFollowOnFragment: + LogPrint(eLogDebug, "SSU2: Follow-on fragment"); + HandleFollowOnFragment(buf + offset, size); + m_IsDataReceived = true; + break; + case eSSU2BlkTermination: + LogPrint(eLogDebug, "SSU2: Termination reason=", (int) buf[11]); + if (IsEstablished() && buf[11] != eSSU2TerminationReasonTerminationReceived) + RequestTermination(eSSU2TerminationReasonTerminationReceived); + else + Done(); + break; + case eSSU2BlkRelayRequest: + LogPrint(eLogDebug, "SSU2: RelayRequest"); + HandleRelayRequest(buf + offset, size); + break; + case eSSU2BlkRelayResponse: + LogPrint(eLogDebug, "SSU2: RelayResponse"); + HandleRelayResponse(buf + offset, size); + break; + case eSSU2BlkRelayIntro: + LogPrint(eLogDebug, "SSU2: RelayIntro"); + HandleRelayIntro(buf + offset, size); + break; + case eSSU2BlkPeerTest: + LogPrint(eLogDebug, "SSU2: PeerTest msg=", (int) buf[offset], " code=", (int) buf[offset + 1]); + HandlePeerTest(buf + offset, size); + if (buf[offset] < 5) + m_IsDataReceived = true; + break; + case eSSU2BlkNextNonce: + break; + case eSSU2BlkAck: + LogPrint(eLogDebug, "SSU2: Ack"); + HandleAck(buf + offset, size); + break; + case eSSU2BlkAddress: + LogPrint(eLogDebug, "SSU2: Address"); + HandleAddress(buf + offset, size); + break; + case eSSU2BlkIntroKey: + break; + case eSSU2BlkRelayTagRequest: + LogPrint(eLogDebug, "SSU2: RelayTagRequest"); + if (!m_RelayTag) { + RAND_bytes((uint8_t * ) & m_RelayTag, 4); + m_Server.AddRelay(m_RelayTag, shared_from_this()); + } + break; + case eSSU2BlkRelayTag: + LogPrint(eLogDebug, "SSU2: RelayTag"); + m_RelayTag = bufbe32toh(buf + offset); + break; + case eSSU2BlkNewToken: { + LogPrint(eLogDebug, "SSU2: New token"); + uint64_t token; + memcpy(&token, buf + offset + 4, 8); + m_Server.UpdateOutgoingToken(m_RemoteEndpoint, token, bufbe32toh(buf + offset)); + break; + } + case eSSU2BlkPathChallenge: + LogPrint(eLogDebug, "SSU2: Path challenge"); + SendPathResponse(buf + offset, size); + break; + case eSSU2BlkPathResponse: + LogPrint(eLogDebug, "SSU2: Path response"); + break; + case eSSU2BlkFirstPacketNumber: + break; + case eSSU2BlkPadding: + LogPrint(eLogDebug, "SSU2: Padding"); + break; + default: + LogPrint(eLogWarning, "SSU2: Unknown block type ", (int) blk); + } + offset += size; + } + } - void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) - { - // we are Bob - uint32_t relayTag = bufbe32toh (buf + 5); // relay tag - auto session = m_Server.FindRelaySession (relayTag); - if (!session) - { - LogPrint (eLogWarning, "SSU2: RelayRequest session with relay tag ", relayTag, " not found"); - // send relay response back to Alice - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize, - eSSU2RelayResponseCodeBobRelayTagNotFound, bufbe32toh (buf + 1), 0, false); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - return; - } - session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce - std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ()) ); + void SSU2Session::HandleDateTime(const uint8_t *buf, size_t len) { + int64_t offset = (int64_t) i2p::util::GetSecondsSinceEpoch() - (int64_t) bufbe32toh(buf); + switch (m_State) { + case eSSU2SessionStateSessionRequestReceived: + case eSSU2SessionStateTokenRequestReceived: + if (std::abs(offset) > SSU2_CLOCK_SKEW) + m_TerminationReason = eSSU2TerminationReasonClockSkew; + break; + case eSSU2SessionStateSessionCreatedReceived: + case eSSU2SessionStateTokenReceived: + if ((m_RemoteEndpoint.address().is_v4() && i2p::context.GetStatus() == eRouterStatusTesting) || + (m_RemoteEndpoint.address().is_v6() && i2p::context.GetStatusV6() == eRouterStatusTesting)) { + if (m_Server.IsSyncClockFromPeers()) { + if (std::abs(offset) > SSU2_CLOCK_THRESHOLD) { + LogPrint(eLogWarning, "SSU2: Clock adjusted by ", -offset, " seconds"); + i2p::util::AdjustTimeOffset(-offset); + } + } else if (std::abs(offset) > SSU2_CLOCK_SKEW) { + LogPrint(eLogError, "SSU2: Clock skew detected ", offset, ". Check your clock"); + i2p::context.SetError(eRouterErrorClockSkew); + } + } + break; + default:; + }; + } - // send relay intro to Charlie - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI - if (r) - i2p::data::netdb.PopulateRouterInfoBuffer (r); - else - LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found"); - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!payloadSize && r) - session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - payloadSize += CreateRelayIntroBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, buf + 1, len -1); - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - session->SendData (payload, payloadSize); - } + void SSU2Session::HandleAck(const uint8_t *buf, size_t len) { + if (m_State == eSSU2SessionStateSessionConfirmedSent) { + Established(); + return; + } + if (m_SentPackets.empty()) return; + if (len < 5) return; + // acnt + uint32_t ackThrough = bufbe32toh(buf); + uint32_t firstPacketNum = ackThrough > buf[4] ? ackThrough - buf[4] : 0; + HandleAckRange(firstPacketNum, ackThrough, i2p::util::GetMillisecondsSinceEpoch()); // acnt + // ranges + len -= 5; + const uint8_t *ranges = buf + 5; + while (len > 0 && firstPacketNum) { + uint32_t lastPacketNum = firstPacketNum - 1; + if (*ranges > lastPacketNum) break; + lastPacketNum -= *ranges; + ranges++; // nacks + if (*ranges > lastPacketNum + 1) break; + firstPacketNum = lastPacketNum - *ranges + 1; + ranges++; // acks + len -= 2; + HandleAckRange(firstPacketNum, lastPacketNum, 0); + } + } - void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len) - { - // we are Charlie - SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; - uint64_t token = 0; - bool isV4 = false; - auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice - if (r) - { - SignedData s; - s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (i2p::context.GetIdentHash (), 32); // chash - s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz - uint8_t asz = buf[46]; - s.Insert (buf + 47, asz); // Alice Port, Alice IP - if (s.Verify (r->GetIdentity (), buf + 47 + asz)) - { - // send HolePunch - boost::asio::ip::udp::endpoint ep; - if (ExtractEndpoint (buf + 47, asz, ep)) - { - auto addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); - if (addr) - { - if (m_Server.IsSupported (ep.address ())) - { - token = m_Server.GetIncomingToken (ep); - isV4 = ep.address ().is_v4 (); - SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro unsupported address"); - code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro unknown address"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro can't extract endpoint"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro signature verification failed"); - code = eSSU2RelayResponseCodeCharlieSignatureFailure; - } - } - else - { - LogPrint (eLogError, "SSU2: RelayIntro unknown router to introduce"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - // send relay response to Bob - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize, - code, bufbe32toh (buf + 33), token, isV4); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - } + void SSU2Session::HandleAckRange(uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts) { + if (firstPacketNum > lastPacketNum) return; + auto it = m_SentPackets.begin(); + while (it != m_SentPackets.end() && it->first < firstPacketNum) it++; // find first acked packet + if (it == m_SentPackets.end() || it->first > lastPacketNum) return; // not found + auto it1 = it; + int numPackets = 0; + while (it1 != m_SentPackets.end() && it1->first <= lastPacketNum) { + if (ts && !it1->second->numResends) { + if (ts > it1->second->sendTime) { + auto rtt = ts - it1->second->sendTime; + m_RTT = (m_RTT * m_SendPacketNum + rtt) / (m_SendPacketNum + 1); + m_RTO = m_RTT * SSU2_kAPPA; + if (m_RTO < SSU2_MIN_RTO) m_RTO = SSU2_MIN_RTO; + if (m_RTO > SSU2_MAX_RTO) m_RTO = SSU2_MAX_RTO; + } + ts = 0; // update RTT one time per range + } + it1++; + numPackets++; + } + m_SentPackets.erase(it, it1); + if (numPackets > 0) { + m_WindowSize += numPackets; + if (m_WindowSize > SSU2_MAX_WINDOW_SIZE) m_WindowSize = SSU2_MAX_WINDOW_SIZE; + } + } - void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len) - { - uint32_t nonce = bufbe32toh (buf + 2); - if (m_State == eSSU2SessionStateIntroduced) - { - // HolePunch from Charlie - // TODO: verify address and signature - // verify nonce - if (~htobe64 (((uint64_t)nonce << 32) | nonce) != m_DestConnID) - LogPrint (eLogWarning, "SSU2: Relay response nonce mismatch ", nonce, " connID=", m_DestConnID); - if (len >= 8) - { - // new token - uint64_t token; - memcpy (&token, buf + len - 8, 8); - m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - } - return; - } - auto it = m_RelaySessions.find (nonce); - if (it != m_RelaySessions.end ()) - { - if (it->second.first && it->second.first->IsEstablished ()) - { - // we are Bob, message from Charlie - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - payload[0] = eSSU2BlkRelayResponse; - htobe16buf (payload + 1, len); - memcpy (payload + 3, buf, len); // forward to Alice as is - size_t payloadSize = len + 3; - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - it->second.first->SendData (payload, payloadSize); - } - else - { - // we are Alice, message from Bob - if (!buf[1]) // status code accepted? - { - // verify signature - uint8_t csz = buf[11]; - SignedData s; - s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint - if (s.Verify (it->second.first->GetRemoteIdentity (), buf + 12 + csz)) - { - if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet - { - // update Charlie's endpoint - if (ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint)) - { - // update token - uint64_t token; - memcpy (&token, buf + len - 8, 8); - m_Server.UpdateOutgoingToken (it->second.first->m_RemoteEndpoint, - token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - // connect to Charlie, HolePunch will be ignored - it->second.first->ConnectAfterIntroduction (); - } - else - LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint"); - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed"); - it->second.first->Done (); - } - } - else - { - LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1]); - it->second.first->Done (); - } - } - m_RelaySessions.erase (it); - } - else - LogPrint (eLogWarning, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2)); - } + void SSU2Session::HandleAddress(const uint8_t *buf, size_t len) { + boost::asio::ip::udp::endpoint ep; + if (ExtractEndpoint(buf, len, ep)) { + LogPrint(eLogInfo, "SSU2: Our external address is ", ep); + if (!i2p::util::net::IsInReservedRange(ep.address())) { + i2p::context.UpdateAddress(ep.address()); + // check our port + bool isV4 = ep.address().is_v4(); + if (ep.port() != m_Server.GetPort(isV4)) { + if (isV4) { + if (i2p::context.GetStatus() == eRouterStatusTesting) + i2p::context.SetError(eRouterErrorSymmetricNAT); + } else { + if (i2p::context.GetStatusV6() == eRouterStatusTesting) + i2p::context.SetErrorV6(eRouterErrorSymmetricNAT); + } + } else { + if (isV4) { + if (i2p::context.GetStatus() == eRouterStatusError && + i2p::context.GetError() == eRouterErrorSymmetricNAT) + i2p::context.SetStatus(eRouterStatusTesting); + } else { + if (i2p::context.GetStatusV6() == eRouterStatusError && + i2p::context.GetErrorV6() == eRouterErrorSymmetricNAT) + i2p::context.SetStatusV6(eRouterStatusTesting); + } + } + } + } + } - void SSU2Session::HandlePeerTest (const uint8_t * buf, size_t len) - { - if (len < 3) return; - uint8_t msg = buf[0]; - size_t offset = 3; // points to signed data - if (msg == 2 || msg == 4) offset += 32; // hash is presented for msg 2 and 4 only - if (len < offset + 5) return; - uint32_t nonce = bufbe32toh (buf + offset + 1); - switch (msg) // msg - { - case 1: // Bob from Alice - { - auto session = m_Server.GetRandomSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, - GetRemoteIdentity ()->GetIdentHash ()); - if (session) // session with Charlie - { - session->m_PeerTests.emplace (nonce, std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ())); - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - // Alice's RouterInfo - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); - if (r) i2p::data::netdb.PopulateRouterInfoBuffer (r); - size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!payloadSize && r) - session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - if (payloadSize + len + 48 > m_MaxPayloadSize) - { - // doesn't fit one message, send RouterInfo in separate message - session->SendData (payload, payloadSize); - payloadSize = 0; - } - // PeerTest to Charlie - payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, 2, - eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - session->SendData (payload, payloadSize); - } - else - { - // Charlie not found, send error back to Alice - uint8_t payload[SSU2_MAX_PACKET_SIZE], zeroHash[32] = {0}; - size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 4, - eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - } - break; - } - case 2: // Charlie from Bob - { - // sign with Charlie's key - uint8_t asz = buf[offset + 9]; - std::vector newSignedData (asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ()); - memcpy (newSignedData.data (), buf + offset, asz + 10); - SignedData s; - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + 3, 32); // ahash - s.Insert (newSignedData.data (), asz + 10); // ver, nonce, ts, asz, Alice's endpoint - s.Sign (i2p::context.GetPrivateKeys (), newSignedData.data () + 10 + asz); - // send response (msg 3) back and msg 5 if accepted - SSU2PeerTestCode code = eSSU2PeerTestCodeAccept; - auto r = i2p::data::netdb.FindRouter (buf + 3); // find Alice - if (r) - { - size_t signatureLen = r->GetIdentity ()->GetSignatureLen (); - if (len >= offset + asz + 10 + signatureLen) - { - s.Reset (); - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + offset, asz + 10); // signed data - if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) - { - if (!m_Server.FindSession (r->GetIdentity ()->GetIdentHash ())) - { - boost::asio::ip::udp::endpoint ep; - std::shared_ptr addr; - if (ExtractEndpoint (buf + offset + 10, asz, ep)) - addr = r->GetSSU2Address (ep.address ().is_v4 ()); - if (addr && m_Server.IsSupported (ep.address ())) - { - // send msg 5 to Alice - auto session = std::make_shared (m_Server, r, addr); - session->SetState (eSSU2SessionStatePeerTest); - session->m_RemoteEndpoint = ep; // might be different - session->m_DestConnID = htobe64 (((uint64_t)nonce << 32) | nonce); - session->m_SourceConnID = ~session->m_DestConnID; - m_Server.AddSession (session); - session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr->i); - } - else - code = eSSU2PeerTestCodeCharlieUnsupportedAddress; - } - else - code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected; - } - else - code = eSSU2PeerTestCodeCharlieSignatureFailure; - } - else // maformed message - code = eSSU2PeerTestCodeCharlieReasonUnspecified; - } - else - code = eSSU2PeerTestCodeCharlieAliceIsUnknown; - // send msg 3 back to Bob - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 3, - code, nullptr, newSignedData.data (), newSignedData.size ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - break; - } - case 3: // Bob from Charlie - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end () && it->second.first) - { - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - // Charlie's RouterInfo - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); - if (r) i2p::data::netdb.PopulateRouterInfoBuffer (r); - size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!payloadSize && r) - it->second.first->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - if (payloadSize + len + 16 > m_MaxPayloadSize) - { - // doesn't fit one message, send RouterInfo in separate message - it->second.first->SendData (payload, payloadSize); - payloadSize = 0; - } - // PeerTest to Alice - payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize, 4, - (SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - it->second.first->SendData (payload, payloadSize); - m_PeerTests.erase (it); - } - else - LogPrint (eLogWarning, "SSU2: Unknown peer test 3 nonce ", nonce); - break; - } - case 4: // Alice from Bob - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - { - if (buf[1] == eSSU2PeerTestCodeAccept) - { - if (GetRouterStatus () == eRouterStatusUnknown) - SetRouterStatus (eRouterStatusTesting); - auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie - if (r && it->second.first) - { - uint8_t asz = buf[offset + 9]; - SignedData s; - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash - s.Insert (buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint - if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) - { - it->second.first->SetRemoteIdentity (r->GetIdentity ()); - auto addr = r->GetSSU2Address (m_Address->IsV4 ()); - if (addr) - { - it->second.first->m_Address = addr; - if (it->second.first->m_State == eSSU2SessionStatePeerTestReceived) - { - // msg 5 already received. send msg 6 - SetRouterStatus (eRouterStatusOK); - it->second.first->m_State = eSSU2SessionStatePeerTest; - it->second.first->SendPeerTest (6, buf + offset, len - offset, addr->i); - } - else - { - if (GetRouterStatus () == eRouterStatusTesting) - { - SetRouterStatus (eRouterStatusFirewalled); - if (m_Address->IsV4 ()) - m_Server.RescheduleIntroducersUpdateTimer (); - else - m_Server.RescheduleIntroducersUpdateTimerV6 (); - } - } - } - else - { - LogPrint (eLogWarning, "SSU2: Peer test 4 address not found"); - it->second.first->Done (); - } - } - else - { - LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed"); - it->second.first->Done (); - } - } - } - else - { - LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1], " from ", - i2p::data::GetIdentHashAbbreviation (buf[1] < 64 ? GetRemoteIdentity ()->GetIdentHash () : i2p::data::IdentHash (buf + 3))); - if (GetRouterStatus () == eRouterStatusTesting) - SetRouterStatus (eRouterStatusUnknown); - it->second.first->Done (); - } - m_PeerTests.erase (it); - } - else - LogPrint (eLogWarning, "SSU2: Unknown peer test 4 nonce ", nonce); - break; - } - case 5: // Alice from Charlie 1 - if (htobe64 (((uint64_t)nonce << 32) | nonce) == m_SourceConnID) - { - if (m_Address) - { - SetRouterStatus (eRouterStatusOK); - SendPeerTest (6, buf + offset, len - offset, m_Address->i); - } - else - // we received msg 5 before msg 4 - m_State = eSSU2SessionStatePeerTestReceived; - } - else - LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", m_SourceConnID); - break; - case 6: // Charlie from Alice - if (m_Address) - SendPeerTest (7, buf + offset, len - offset, m_Address->i); - else - LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); - m_Server.RemoveSession (~htobe64 (((uint64_t)nonce << 32) | nonce)); - break; - case 7: // Alice from Charlie 2 - m_Server.RemoveSession (htobe64 (((uint64_t)nonce << 32) | nonce)); - if (m_Address->IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2 - break; - default: - LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]); - } - } + void SSU2Session::HandleFirstFragment(const uint8_t *buf, size_t len) { + uint32_t msgID; + memcpy(&msgID, buf + 1, 4); + auto msg = NewI2NPShortMessage(); + // same format as I2NP message block + msg->len = msg->offset + len + 7; + memcpy(msg->GetNTCP2Header(), buf, len); + std::shared_ptr m; + bool found = false; + auto it = m_IncompleteMessages.find(msgID); + if (it != m_IncompleteMessages.end()) { + found = true; + m = it->second; + } else { + m = std::make_shared(); + m_IncompleteMessages.emplace(msgID, m); + } + m->msg = msg; + m->nextFragmentNum = 1; + m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch(); + if (found && ConcatOutOfSequenceFragments(m)) { + // we have all follow-on fragments already + m->msg->FromNTCP2(); + m_Handler.PutNextMessage(std::move(m->msg)); + m_IncompleteMessages.erase(it); + } + } - bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep) - { - if (size < 2) return false; - int port = bufbe16toh (buf); - if (size == 6) - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), buf + 2, 4); - ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port); - } - else if (size == 18) - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), buf + 2, 16); - ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v6 (bytes), port); - } - else - { - LogPrint (eLogWarning, "SSU2: Address size ", int(size), " is not supported"); - return false; - } - return true; - } + void SSU2Session::HandleFollowOnFragment(const uint8_t *buf, size_t len) { + if (len < 5) return; + uint8_t fragmentNum = buf[0] >> 1; + bool isLast = buf[0] & 0x01; + uint32_t msgID; + memcpy(&msgID, buf + 1, 4); + auto it = m_IncompleteMessages.find(msgID); + if (it != m_IncompleteMessages.end()) { + if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS && + it->second->msg) { + // in sequence + it->second->AttachNextFragment(buf + 5, len - 5); + if (isLast) { + it->second->msg->FromNTCP2(); + m_Handler.PutNextMessage(std::move(it->second->msg)); + m_IncompleteMessages.erase(it); + } else { + if (ConcatOutOfSequenceFragments(it->second)) { + m_Handler.PutNextMessage(std::move(it->second->msg)); + m_IncompleteMessages.erase(it); + } else + it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch(); + } + return; + } + } else { + // follow-on fragment before first fragment + auto msg = std::make_shared(); + msg->nextFragmentNum = 0; + it = m_IncompleteMessages.emplace(msgID, msg).first; + } + // insert out of sequence fragment + if (fragmentNum >= SSU2_MAX_NUM_FRAGMENTS) { + LogPrint(eLogWarning, "SSU2: Fragment number ", fragmentNum, " exceeds ", SSU2_MAX_NUM_FRAGMENTS); + return; + } + auto fragment = std::make_shared(); + memcpy(fragment->buf, buf + 5, len - 5); + fragment->len = len - 5; + fragment->isLast = isLast; + it->second->outOfSequenceFragments.emplace(fragmentNum, fragment); + it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch(); + } - size_t SSU2Session::CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) - { - if (len < 6) return 0; - htobe16buf (buf, ep.port ()); - size_t size = 0; - if (ep.address ().is_v4 ()) - { - memcpy (buf + 2, ep.address ().to_v4 ().to_bytes ().data (), 4); - size = 6; - } - else if (ep.address ().is_v6 ()) - { - if (len < 18) return 0; - memcpy (buf + 2, ep.address ().to_v6 ().to_bytes ().data (), 16); - size = 18; - } - else - { - LogPrint (eLogWarning, "SSU2: Wrong address type ", ep.address ().to_string ()); - return 0; - } - return size; - } + bool SSU2Session::ConcatOutOfSequenceFragments(std::shared_ptr m) { + if (!m) return false; + bool isLast = false; + for (auto it = m->outOfSequenceFragments.begin(); it != m->outOfSequenceFragments.end();) + if (it->first == m->nextFragmentNum) { + m->AttachNextFragment(it->second->buf, it->second->len); + isLast = it->second->isLast; + it = m->outOfSequenceFragments.erase(it); + } else + break; + return isLast; + } - std::shared_ptr SSU2Session::FindLocalAddress () const - { - if (m_Address) - return i2p::context.GetRouterInfo ().GetSSU2Address (m_Address->IsV4 ()); - return nullptr; - } + void SSU2Session::HandleRelayRequest(const uint8_t *buf, size_t len) { + // we are Bob + uint32_t relayTag = bufbe32toh(buf + 5); // relay tag + auto session = m_Server.FindRelaySession(relayTag); + if (!session) { + LogPrint(eLogWarning, "SSU2: RelayRequest session with relay tag ", relayTag, " not found"); + // send relay response back to Alice + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateRelayResponseBlock(payload, m_MaxPayloadSize, + eSSU2RelayResponseCodeBobRelayTagNotFound, + bufbe32toh(buf + 1), 0, false); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData(payload, payloadSize); + return; + } + session->m_RelaySessions.emplace(bufbe32toh(buf + 1), // nonce + std::make_pair(shared_from_this(), i2p::util::GetSecondsSinceEpoch())); - void SSU2Session::AdjustMaxPayloadSize () - { - auto addr = FindLocalAddress (); - if (addr && addr->ssu) - { - int mtu = addr->ssu->mtu; - if (!mtu && addr->IsV4 ()) mtu = SSU2_MAX_PACKET_SIZE; - if (m_Address && m_Address->ssu && (!mtu || m_Address->ssu->mtu < mtu)) - mtu = m_Address->ssu->mtu; - if (mtu) - { - m_MaxPayloadSize = mtu - (addr->IsV6 () ? IPV6_HEADER_SIZE: IPV4_HEADER_SIZE) - UDP_HEADER_SIZE - 32; - LogPrint (eLogDebug, "SSU2: Session MTU=", mtu, ", max payload size=", m_MaxPayloadSize); - } - } - } - - RouterStatus SSU2Session::GetRouterStatus () const - { - if (m_Address) - { - if (m_Address->IsV4 ()) - return i2p::context.GetStatus (); - if (m_Address->IsV6 ()) - return i2p::context.GetStatusV6 (); - } - return eRouterStatusUnknown; - } - - void SSU2Session::SetRouterStatus (RouterStatus status) const - { - if (m_Address) - { - if (m_Address->IsV4 ()) - i2p::context.SetStatusSSU2 (status); - else if (m_Address->IsV6 ()) - i2p::context.SetStatusV6SSU2 (status); - } - } - - size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) - { - if (len < 9) return 0; - buf[0] = eSSU2BlkAddress; - size_t size = CreateEndpoint (buf + 3, len - 3, ep); - if (!size) return 0; - htobe16buf (buf + 1, size); - return size + 3; - } + // send relay intro to Charlie + auto r = i2p::data::netdb.FindRouter(GetRemoteIdentity()->GetIdentHash()); // Alice's RI + if (r) + i2p::data::netdb.PopulateRouterInfoBuffer(r); + else + LogPrint(eLogWarning, "SSU2: RelayRequest Alice's router info not found"); + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = r ? CreateRouterInfoBlock(payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage(CreateDatabaseStoreMsg(r)); + payloadSize += CreateRelayIntroBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize, buf + 1, + len - 1); + if (payloadSize < m_MaxPayloadSize) + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + session->SendData(payload, payloadSize); + } - size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r) - { - if (!r || !r->GetBuffer () || len < 5) return 0; - buf[0] = eSSU2BlkRouterInfo; - size_t size = r->GetBufferLen (); - if (size + 5 < len) - { - memcpy (buf + 5, r->GetBuffer (), size); - buf[3] = 0; // flag - } - else - { - i2p::data::GzipDeflator deflator; - deflator.SetCompressionLevel (9); - size = deflator.Deflate (r->GetBuffer (), r->GetBufferLen (), buf + 5, len - 5); - if (!size) return 0; // doesn't fit - buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag - } - htobe16buf (buf + 1, size + 2); // size - buf[4] = 1; // frag - return size + 5; - } + void SSU2Session::HandleRelayIntro(const uint8_t *buf, size_t len) { + // we are Charlie + SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; + uint64_t token = 0; + bool isV4 = false; + auto r = i2p::data::netdb.FindRouter(buf + 1); // Alice + if (r) { + SignedData s; + s.Insert((const uint8_t *) "RelayRequestData", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(i2p::context.GetIdentHash(), 32); // chash + s.Insert(buf + 33, 14); // nonce, relay tag, timestamp, ver, asz + uint8_t asz = buf[46]; + s.Insert(buf + 47, asz); // Alice Port, Alice IP + if (s.Verify(r->GetIdentity(), buf + 47 + asz)) { + // send HolePunch + boost::asio::ip::udp::endpoint ep; + if (ExtractEndpoint(buf + 47, asz, ep)) { + auto addr = ep.address().is_v6() ? r->GetSSU2V6Address() : r->GetSSU2V4Address(); + if (addr) { + if (m_Server.IsSupported(ep.address())) { + token = m_Server.GetIncomingToken(ep); + isV4 = ep.address().is_v4(); + SendHolePunch(bufbe32toh(buf + 33), ep, addr->i, token); + } else { + LogPrint(eLogWarning, "SSU2: RelayIntro unsupported address"); + code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; + } + } else { + LogPrint(eLogWarning, "SSU2: RelayIntro unknown address"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } + } else { + LogPrint(eLogWarning, "SSU2: RelayIntro can't extract endpoint"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } + } else { + LogPrint(eLogWarning, "SSU2: RelayIntro signature verification failed"); + code = eSSU2RelayResponseCodeCharlieSignatureFailure; + } + } else { + LogPrint(eLogError, "SSU2: RelayIntro unknown router to introduce"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } + // send relay response to Bob + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateRelayResponseBlock(payload, m_MaxPayloadSize, + code, bufbe32toh(buf + 33), token, isV4); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData(payload, payloadSize); + } - size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len) - { - if (len < 8) return 0; - int maxNumRanges = (len - 8) >> 1; - if (maxNumRanges > SSU2_MAX_NUM_ACK_RANGES) maxNumRanges = SSU2_MAX_NUM_ACK_RANGES; - buf[0] = eSSU2BlkAck; - uint32_t ackThrough = m_OutOfSequencePackets.empty () ? m_ReceivePacketNum : *m_OutOfSequencePackets.rbegin (); - htobe32buf (buf + 3, ackThrough); // Ack Through - uint16_t acnt = 0; - int numRanges = 0; - if (ackThrough) - { - if (m_OutOfSequencePackets.empty ()) - acnt = std::min ((int)ackThrough, 255); // no gaps - else - { - auto it = m_OutOfSequencePackets.rbegin (); it++; // prev packet num - while (it != m_OutOfSequencePackets.rend () && *it == ackThrough - acnt - 1) - { - acnt++; - it++; - } - // ranges - uint32_t lastNum = ackThrough - acnt; - if (acnt > 255) - { - auto d = std::div (acnt - 255, 255); - acnt = 255; - if (d.quot > maxNumRanges) - { - d.quot = maxNumRanges; - d.rem = 0; - } - // Acks only ragnes for acnt - for (int i = 0; i < d.quot; i++) - { - buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = 255; // NACKs 0, Acks 255 - numRanges++; - } - if (d.rem > 0) - { - buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = d.rem; - numRanges++; - } - } - while (it != m_OutOfSequencePackets.rend () && numRanges < maxNumRanges) - { - if (lastNum - (*it) > 255) - { - // NACKs only ranges - if (lastNum > (*it) + 255*(maxNumRanges - numRanges)) break; // too many NACKs - while (lastNum - (*it) > 255) - { - buf[8 + numRanges*2] = 255; buf[8 + numRanges*2 + 1] = 0; // NACKs 255, Acks 0 - lastNum -= 255; - numRanges++; - } - } - // NACKs and Acks ranges - buf[8 + numRanges*2] = lastNum - (*it) - 1; // NACKs - lastNum = *it; it++; - int numAcks = 1; - while (it != m_OutOfSequencePackets.rend () && lastNum > 0 && *it == lastNum - 1) - { - numAcks++; lastNum--; - it++; - } - while (numAcks > 255) - { - // Acks only ranges - buf[8 + numRanges*2 + 1] = 255; // Acks 255 - numAcks -= 255; - numRanges++; - buf[8 + numRanges*2] = 0; // NACKs 0 - if (numRanges >= maxNumRanges) break; - } - if (numAcks > 255) numAcks = 255; - buf[8 + numRanges*2 + 1] = (uint8_t)numAcks; // Acks - numRanges++; - } - if (numRanges < maxNumRanges && it == m_OutOfSequencePackets.rend ()) - { - // add range between out-of-seqence and received - int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1; - if (nacks > 0) - { - if (nacks > 255) nacks = 255; - buf[8 + numRanges*2] = nacks; - buf[8 + numRanges*2 + 1] = std::min ((int)m_ReceivePacketNum + 1, 255); - numRanges++; - } - } - } - } - buf[7] = (uint8_t)acnt; // acnt - htobe16buf (buf + 1, 5 + numRanges*2); - return 8 + numRanges*2; - } + void SSU2Session::HandleRelayResponse(const uint8_t *buf, size_t len) { + uint32_t nonce = bufbe32toh(buf + 2); + if (m_State == eSSU2SessionStateIntroduced) { + // HolePunch from Charlie + // TODO: verify address and signature + // verify nonce + if (~htobe64(((uint64_t) nonce << 32) | nonce) != m_DestConnID) + LogPrint(eLogWarning, "SSU2: Relay response nonce mismatch ", nonce, " connID=", m_DestConnID); + if (len >= 8) { + // new token + uint64_t token; + memcpy(&token, buf + len - 8, 8); + m_Server.UpdateOutgoingToken(m_RemoteEndpoint, token, + i2p::util::GetSecondsSinceEpoch() + SSU2_TOKEN_EXPIRATION_TIMEOUT); + } + return; + } + auto it = m_RelaySessions.find(nonce); + if (it != m_RelaySessions.end()) { + if (it->second.first && it->second.first->IsEstablished()) { + // we are Bob, message from Charlie + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + payload[0] = eSSU2BlkRelayResponse; + htobe16buf(payload + 1, len); + memcpy(payload + 3, buf, len); // forward to Alice as is + size_t payloadSize = len + 3; + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + it->second.first->SendData(payload, payloadSize); + } else { + // we are Alice, message from Bob + if (!buf[1]) // status code accepted? + { + // verify signature + uint8_t csz = buf[11]; + SignedData s; + s.Insert((const uint8_t *) "RelayAgreementOK", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint + if (s.Verify(it->second.first->GetRemoteIdentity(), buf + 12 + csz)) { + if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet + { + // update Charlie's endpoint + if (ExtractEndpoint(buf + 12, csz, it->second.first->m_RemoteEndpoint)) { + // update token + uint64_t token; + memcpy(&token, buf + len - 8, 8); + m_Server.UpdateOutgoingToken(it->second.first->m_RemoteEndpoint, + token, i2p::util::GetSecondsSinceEpoch() + + SSU2_TOKEN_EXPIRATION_TIMEOUT); + // connect to Charlie, HolePunch will be ignored + it->second.first->ConnectAfterIntroduction(); + } else + LogPrint(eLogWarning, "SSU2: RelayResponse can't extract endpoint"); + } + } else { + LogPrint(eLogWarning, "SSU2: RelayResponse signature verification failed"); + it->second.first->Done(); + } + } else { + LogPrint(eLogInfo, "SSU2: RelayResponse status code=", (int) buf[1]); + it->second.first->Done(); + } + } + m_RelaySessions.erase(it); + } else + LogPrint(eLogWarning, "SSU2: RelayResponse unknown nonce ", bufbe32toh(buf + 2)); + } - size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize) - { - if (len < minSize) return 0; - uint8_t paddingSize = rand () & 0x0F; // 0 - 15 - if (paddingSize > len) paddingSize = len; - else if (paddingSize < minSize) paddingSize = minSize; - if (paddingSize) - { - buf[0] = eSSU2BlkPadding; - htobe16buf (buf + 1, paddingSize); - memset (buf + 3, 0, paddingSize); - } - else - return 0; - return paddingSize + 3; - } + void SSU2Session::HandlePeerTest(const uint8_t *buf, size_t len) { + if (len < 3) return; + uint8_t msg = buf[0]; + size_t offset = 3; // points to signed data + if (msg == 2 || msg == 4) offset += 32; // hash is presented for msg 2 and 4 only + if (len < offset + 5) return; + uint32_t nonce = bufbe32toh(buf + offset + 1); + switch (msg) // msg + { + case 1: // Bob from Alice + { + auto session = m_Server.GetRandomSession( + (buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, + GetRemoteIdentity()->GetIdentHash()); + if (session) // session with Charlie + { + session->m_PeerTests.emplace(nonce, std::make_pair(shared_from_this(), + i2p::util::GetSecondsSinceEpoch())); + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + // Alice's RouterInfo + auto r = i2p::data::netdb.FindRouter(GetRemoteIdentity()->GetIdentHash()); + if (r) i2p::data::netdb.PopulateRouterInfoBuffer(r); + size_t payloadSize = r ? CreateRouterInfoBlock(payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage(CreateDatabaseStoreMsg(r)); + if (payloadSize + len + 48 > m_MaxPayloadSize) { + // doesn't fit one message, send RouterInfo in separate message + session->SendData(payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Charlie + payloadSize += CreatePeerTestBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize, 2, + eSSU2PeerTestCodeAccept, GetRemoteIdentity()->GetIdentHash(), + buf + offset, len - offset); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + session->SendData(payload, payloadSize); + } else { + // Charlie not found, send error back to Alice + uint8_t payload[SSU2_MAX_PACKET_SIZE], zeroHash[32] = {0}; + size_t payloadSize = CreatePeerTestBlock(payload, m_MaxPayloadSize, 4, + eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, + buf + offset, len - offset); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData(payload, payloadSize); + } + break; + } + case 2: // Charlie from Bob + { + // sign with Charlie's key + uint8_t asz = buf[offset + 9]; + std::vector newSignedData(asz + 10 + i2p::context.GetIdentity()->GetSignatureLen()); + memcpy(newSignedData.data(), buf + offset, asz + 10); + SignedData s; + s.Insert((const uint8_t *) "PeerTestValidate", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(buf + 3, 32); // ahash + s.Insert(newSignedData.data(), asz + 10); // ver, nonce, ts, asz, Alice's endpoint + s.Sign(i2p::context.GetPrivateKeys(), newSignedData.data() + 10 + asz); + // send response (msg 3) back and msg 5 if accepted + SSU2PeerTestCode code = eSSU2PeerTestCodeAccept; + auto r = i2p::data::netdb.FindRouter(buf + 3); // find Alice + if (r) { + size_t signatureLen = r->GetIdentity()->GetSignatureLen(); + if (len >= offset + asz + 10 + signatureLen) { + s.Reset(); + s.Insert((const uint8_t *) "PeerTestValidate", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(buf + offset, asz + 10); // signed data + if (s.Verify(r->GetIdentity(), buf + offset + asz + 10)) { + if (!m_Server.FindSession(r->GetIdentity()->GetIdentHash())) { + boost::asio::ip::udp::endpoint ep; + std::shared_ptr addr; + if (ExtractEndpoint(buf + offset + 10, asz, ep)) + addr = r->GetSSU2Address(ep.address().is_v4()); + if (addr && m_Server.IsSupported(ep.address())) { + // send msg 5 to Alice + auto session = std::make_shared(m_Server, r, addr); + session->SetState(eSSU2SessionStatePeerTest); + session->m_RemoteEndpoint = ep; // might be different + session->m_DestConnID = htobe64(((uint64_t) nonce << 32) | nonce); + session->m_SourceConnID = ~session->m_DestConnID; + m_Server.AddSession(session); + session->SendPeerTest(5, newSignedData.data(), newSignedData.size(), addr->i); + } else + code = eSSU2PeerTestCodeCharlieUnsupportedAddress; + } else + code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected; + } else + code = eSSU2PeerTestCodeCharlieSignatureFailure; + } else // maformed message + code = eSSU2PeerTestCodeCharlieReasonUnspecified; + } else + code = eSSU2PeerTestCodeCharlieAliceIsUnknown; + // send msg 3 back to Bob + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreatePeerTestBlock(payload, m_MaxPayloadSize, 3, + code, nullptr, newSignedData.data(), newSignedData.size()); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData(payload, payloadSize); + break; + } + case 3: // Bob from Charlie + { + auto it = m_PeerTests.find(nonce); + if (it != m_PeerTests.end() && it->second.first) { + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + // Charlie's RouterInfo + auto r = i2p::data::netdb.FindRouter(GetRemoteIdentity()->GetIdentHash()); + if (r) i2p::data::netdb.PopulateRouterInfoBuffer(r); + size_t payloadSize = r ? CreateRouterInfoBlock(payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!payloadSize && r) + it->second.first->SendFragmentedMessage(CreateDatabaseStoreMsg(r)); + if (payloadSize + len + 16 > m_MaxPayloadSize) { + // doesn't fit one message, send RouterInfo in separate message + it->second.first->SendData(payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Alice + payloadSize += CreatePeerTestBlock(payload + payloadSize, m_MaxPayloadSize, 4, + (SSU2PeerTestCode) buf[1], + GetRemoteIdentity()->GetIdentHash(), buf + offset, + len - offset); + if (payloadSize < m_MaxPayloadSize) + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + it->second.first->SendData(payload, payloadSize); + m_PeerTests.erase(it); + } else + LogPrint(eLogWarning, "SSU2: Unknown peer test 3 nonce ", nonce); + break; + } + case 4: // Alice from Bob + { + auto it = m_PeerTests.find(nonce); + if (it != m_PeerTests.end()) { + if (buf[1] == eSSU2PeerTestCodeAccept) { + if (GetRouterStatus() == eRouterStatusUnknown) + SetRouterStatus(eRouterStatusTesting); + auto r = i2p::data::netdb.FindRouter(buf + 3); // find Charlie + if (r && it->second.first) { + uint8_t asz = buf[offset + 9]; + SignedData s; + s.Insert((const uint8_t *) "PeerTestValidate", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(i2p::context.GetIdentity()->GetIdentHash(), 32); // ahash + s.Insert(buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint + if (s.Verify(r->GetIdentity(), buf + offset + asz + 10)) { + it->second.first->SetRemoteIdentity(r->GetIdentity()); + auto addr = r->GetSSU2Address(m_Address->IsV4()); + if (addr) { + it->second.first->m_Address = addr; + if (it->second.first->m_State == eSSU2SessionStatePeerTestReceived) { + // msg 5 already received. send msg 6 + SetRouterStatus(eRouterStatusOK); + it->second.first->m_State = eSSU2SessionStatePeerTest; + it->second.first->SendPeerTest(6, buf + offset, len - offset, addr->i); + } else { + if (GetRouterStatus() == eRouterStatusTesting) { + SetRouterStatus(eRouterStatusFirewalled); + if (m_Address->IsV4()) + m_Server.RescheduleIntroducersUpdateTimer(); + else + m_Server.RescheduleIntroducersUpdateTimerV6(); + } + } + } else { + LogPrint(eLogWarning, "SSU2: Peer test 4 address not found"); + it->second.first->Done(); + } + } else { + LogPrint(eLogWarning, "SSU2: Peer test 4 signature verification failed"); + it->second.first->Done(); + } + } + } else { + LogPrint(eLogInfo, "SSU2: Peer test 4 error code ", (int) buf[1], " from ", + i2p::data::GetIdentHashAbbreviation( + buf[1] < 64 ? GetRemoteIdentity()->GetIdentHash() : i2p::data::IdentHash( + buf + 3))); + if (GetRouterStatus() == eRouterStatusTesting) + SetRouterStatus(eRouterStatusUnknown); + it->second.first->Done(); + } + m_PeerTests.erase(it); + } else + LogPrint(eLogWarning, "SSU2: Unknown peer test 4 nonce ", nonce); + break; + } + case 5: // Alice from Charlie 1 + if (htobe64(((uint64_t) nonce << 32) | nonce) == m_SourceConnID) { + if (m_Address) { + SetRouterStatus(eRouterStatusOK); + SendPeerTest(6, buf + offset, len - offset, m_Address->i); + } else + // we received msg 5 before msg 4 + m_State = eSSU2SessionStatePeerTestReceived; + } else + LogPrint(eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", m_SourceConnID); + break; + case 6: // Charlie from Alice + if (m_Address) + SendPeerTest(7, buf + offset, len - offset, m_Address->i); + else + LogPrint(eLogWarning, "SSU2: Unknown address for peer test 6"); + m_Server.RemoveSession(~htobe64(((uint64_t) nonce << 32) | nonce)); + break; + case 7: // Alice from Charlie 2 + m_Server.RemoveSession(htobe64(((uint64_t) nonce << 32) | nonce)); + if (m_Address->IsV6()) + i2p::context.SetStatusV6(eRouterStatusOK); // set status OK for ipv6 even if from SSU2 + break; + default: + LogPrint(eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]); + } + } - size_t SSU2Session::CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg) - { - msg->ToNTCP2 (); - auto msgBuf = msg->GetNTCP2Header (); - auto msgLen = msg->GetNTCP2Length (); - if (msgLen + 3 > len) msgLen = len - 3; - buf[0] = eSSU2BlkI2NPMessage; - htobe16buf (buf + 1, msgLen); // size - memcpy (buf + 3, msgBuf, msgLen); - return msgLen + 3; - } + bool SSU2Session::ExtractEndpoint(const uint8_t *buf, size_t size, boost::asio::ip::udp::endpoint &ep) { + if (size < 2) return false; + int port = bufbe16toh(buf); + if (size == 6) { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy(bytes.data(), buf + 2, 4); + ep = boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4(bytes), port); + } else if (size == 18) { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy(bytes.data(), buf + 2, 16); + ep = boost::asio::ip::udp::endpoint(boost::asio::ip::address_v6(bytes), port); + } else { + LogPrint(eLogWarning, "SSU2: Address size ", int(size), " is not supported"); + return false; + } + return true; + } - size_t SSU2Session::CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg) - { - if (len < 12) return 0; - msg->ToNTCP2 (); - auto msgBuf = msg->GetNTCP2Header (); - auto msgLen = msg->GetNTCP2Length (); - if (msgLen + 3 <= len) return 0; - msgLen = len - 3; - buf[0] = eSSU2BlkFirstFragment; - htobe16buf (buf + 1, msgLen); // size - memcpy (buf + 3, msgBuf, msgLen); - msg->offset = (msgBuf - msg->buf) + msgLen; - return msgLen + 3; - } + size_t SSU2Session::CreateEndpoint(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &ep) { + if (len < 6) return 0; + htobe16buf(buf, ep.port()); + size_t size = 0; + if (ep.address().is_v4()) { + memcpy(buf + 2, ep.address().to_v4().to_bytes().data(), 4); + size = 6; + } else if (ep.address().is_v6()) { + if (len < 18) return 0; + memcpy(buf + 2, ep.address().to_v6().to_bytes().data(), 16); + size = 18; + } else { + LogPrint(eLogWarning, "SSU2: Wrong address type ", ep.address().to_string()); + return 0; + } + return size; + } - size_t SSU2Session::CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID) - { - if (len < 8) return 0; - bool isLast = true; - auto msgLen = msg->len - msg->offset; - if (msgLen + 8 > len) - { - msgLen = len - 8; - isLast = false; - } - buf[0] = eSSU2BlkFollowOnFragment; - htobe16buf (buf + 1, msgLen + 5); // size - fragmentNum++; - buf[3] = fragmentNum << 1; - if (isLast) buf[3] |= 0x01; - memcpy (buf + 4, &msgID, 4); - memcpy (buf + 8, msg->buf + msg->offset, msgLen); - msg->offset += msgLen; - return msgLen + 8; - } + std::shared_ptr SSU2Session::FindLocalAddress() const { + if (m_Address) + return i2p::context.GetRouterInfo().GetSSU2Address(m_Address->IsV4()); + return nullptr; + } - size_t SSU2Session::CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen) - { - buf[0] = eSSU2BlkRelayIntro; - size_t payloadSize = 1/* flag */ + 32/* Alice router hash */ + introDataLen; - if (payloadSize + 3 > len) return 0; - htobe16buf (buf + 1, payloadSize); // size - buf[3] = 0; // flag - memcpy (buf + 4, GetRemoteIdentity ()->GetIdentHash (), 32); // Alice router hash - memcpy (buf + 36, introData, introDataLen); - return payloadSize + 3; - } + void SSU2Session::AdjustMaxPayloadSize() { + auto addr = FindLocalAddress(); + if (addr && addr->ssu) { + int mtu = addr->ssu->mtu; + if (!mtu && addr->IsV4()) mtu = SSU2_MAX_PACKET_SIZE; + if (m_Address && m_Address->ssu && (!mtu || m_Address->ssu->mtu < mtu)) + mtu = m_Address->ssu->mtu; + if (mtu) { + m_MaxPayloadSize = + mtu - (addr->IsV6() ? IPV6_HEADER_SIZE : IPV4_HEADER_SIZE) - UDP_HEADER_SIZE - 32; + LogPrint(eLogDebug, "SSU2: Session MTU=", mtu, ", max payload size=", m_MaxPayloadSize); + } + } + } - size_t SSU2Session::CreateRelayResponseBlock (uint8_t * buf, size_t len, - SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4) - { - buf[0] = eSSU2BlkRelayResponse; - buf[3] = 0; // flag - buf[4] = code; // code - htobe32buf (buf + 5, nonce); // nonce - htobe32buf (buf + 9, i2p::util::GetSecondsSinceEpoch ()); // timestamp - buf[13] = 2; // ver - size_t csz = 0; - if (code == eSSU2RelayResponseCodeAccept) - { - auto addr = i2p::context.GetRouterInfo ().GetSSU2Address (v4); - if (!addr) - { - LogPrint (eLogError, "SSU2: Can't find local address for RelayResponse"); - return 0; - } - csz = CreateEndpoint (buf + 15, len - 15, boost::asio::ip::udp::endpoint (addr->host, addr->port)); - if (!csz) - { - LogPrint (eLogError, "SSU2: Can't create local endpoint for RelayResponse"); - return 0; - } - } - buf[14] = csz; // csz - // signature - size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); - if (15 + csz + signatureLen > len) - { - LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len); - return 0; - } - SignedData s; - s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue - if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - else // Bob's reject - s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + 5, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint - s.Sign (i2p::context.GetPrivateKeys (), buf + 15 + csz); - size_t payloadSize = 12 + csz + signatureLen; - if (!code) - { - if (payloadSize + 11 > len) - { - LogPrint (eLogError, "SSU2: Buffer for RelayResponse token is too small ", len); - return 0; - } - memcpy (buf + 3 + payloadSize, &token, 8); - payloadSize += 8; - } - htobe16buf (buf + 1, payloadSize); // size - return payloadSize + 3; - } - - size_t SSU2Session::CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, - const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen) - { - buf[0] = eSSU2BlkPeerTest; - size_t payloadSize = 3/* msg, code, flag */ + signedDataLen; - if (routerHash) payloadSize += 32; // router hash - if (payloadSize + 3 > len) return 0; - htobe16buf (buf + 1, payloadSize); // size - buf[3] = msg; // msg - buf[4] = (uint8_t)code; // code - buf[5] = 0; //flag - size_t offset = 6; - if (routerHash) - { - memcpy (buf + offset, routerHash, 32); // router hash - offset += 32; - } - memcpy (buf + offset, signedData, signedDataLen); - return payloadSize + 3; - } + RouterStatus SSU2Session::GetRouterStatus() const { + if (m_Address) { + if (m_Address->IsV4()) + return i2p::context.GetStatus(); + if (m_Address->IsV6()) + return i2p::context.GetStatusV6(); + } + return eRouterStatusUnknown; + } - size_t SSU2Session::CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce) - { - auto localAddress = FindLocalAddress (); - if (!localAddress || !localAddress->port || localAddress->host.is_unspecified () || - localAddress->host.is_v4 () != m_RemoteEndpoint.address ().is_v4 ()) - { - LogPrint (eLogWarning, "SSU2: Can't find local address for peer test"); - return 0; - } - // signed data - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint8_t signedData[96]; - signedData[0] = 2; // ver - htobe32buf (signedData + 1, nonce); - htobe32buf (signedData + 5, ts); - size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); - signedData[9] = asz; - // signature - SignedData s; - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint - s.Sign (i2p::context.GetPrivateKeys (), signedData + 10 + asz); - return CreatePeerTestBlock (buf, len, 1, eSSU2PeerTestCodeAccept, nullptr, - signedData, 10 + asz + i2p::context.GetIdentity ()->GetSignatureLen ()); - } + void SSU2Session::SetRouterStatus(RouterStatus status) const { + if (m_Address) { + if (m_Address->IsV4()) + i2p::context.SetStatusSSU2(status); + else if (m_Address->IsV6()) + i2p::context.SetStatusV6SSU2(status); + } + } - size_t SSU2Session::CreateTerminationBlock (uint8_t * buf, size_t len) - { - buf[0] = eSSU2BlkTermination; - htobe16buf (buf + 1, 9); - htobe64buf (buf + 3, m_ReceivePacketNum); - buf[11] = (uint8_t)m_TerminationReason; - return 12; - } - - std::shared_ptr SSU2Session::ExtractRouterInfo (const uint8_t * buf, size_t size) - { - if (size < 2) return nullptr; - // TODO: handle frag - std::shared_ptr ri; - if (buf[0] & SSU2_ROUTER_INFO_FLAG_GZIP) - { - i2p::data::GzipInflator inflator; - uint8_t uncompressed[i2p::data::MAX_RI_BUFFER_SIZE]; - size_t uncompressedSize = inflator.Inflate (buf + 2, size - 2, uncompressed, i2p::data::MAX_RI_BUFFER_SIZE); - if (uncompressedSize && uncompressedSize < i2p::data::MAX_RI_BUFFER_SIZE) - ri = std::make_shared(uncompressed, uncompressedSize); - else - LogPrint (eLogInfo, "SSU2: RouterInfo decompression failed ", uncompressedSize); - } - else - ri = std::make_shared(buf + 2, size - 2); - return ri; - } + size_t SSU2Session::CreateAddressBlock(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &ep) { + if (len < 9) return 0; + buf[0] = eSSU2BlkAddress; + size_t size = CreateEndpoint(buf + 3, len - 3, ep); + if (!size) return 0; + htobe16buf(buf + 1, size); + return size + 3; + } - void SSU2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } + size_t + SSU2Session::CreateRouterInfoBlock(uint8_t *buf, size_t len, std::shared_ptr r) { + if (!r || !r->GetBuffer() || len < 5) return 0; + buf[0] = eSSU2BlkRouterInfo; + size_t size = r->GetBufferLen(); + if (size + 5 < len) { + memcpy(buf + 5, r->GetBuffer(), size); + buf[3] = 0; // flag + } else { + i2p::data::GzipDeflator deflator; + deflator.SetCompressionLevel(9); + size = deflator.Deflate(r->GetBuffer(), r->GetBufferLen(), buf + 5, len - 5); + if (!size) return 0; // doesn't fit + buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag + } + htobe16buf(buf + 1, size + 2); // size + buf[4] = 1; // frag + return size + 5; + } - bool SSU2Session::UpdateReceivePacketNum (uint32_t packetNum) - { - if (packetNum <= m_ReceivePacketNum) return false; // duplicate - if (packetNum == m_ReceivePacketNum + 1) - { - for (auto it = m_OutOfSequencePackets.begin (); it != m_OutOfSequencePackets.end ();) - { - if (*it == packetNum + 1) - { - packetNum++; - it = m_OutOfSequencePackets.erase (it); - } - else - break; - } - m_ReceivePacketNum = packetNum; - } - else - m_OutOfSequencePackets.insert (packetNum); - return true; - } + size_t SSU2Session::CreateAckBlock(uint8_t *buf, size_t len) { + if (len < 8) return 0; + int maxNumRanges = (len - 8) >> 1; + if (maxNumRanges > SSU2_MAX_NUM_ACK_RANGES) maxNumRanges = SSU2_MAX_NUM_ACK_RANGES; + buf[0] = eSSU2BlkAck; + uint32_t ackThrough = m_OutOfSequencePackets.empty() ? m_ReceivePacketNum + : *m_OutOfSequencePackets.rbegin(); + htobe32buf(buf + 3, ackThrough); // Ack Through + uint16_t acnt = 0; + int numRanges = 0; + if (ackThrough) { + if (m_OutOfSequencePackets.empty()) + acnt = std::min((int) ackThrough, 255); // no gaps + else { + auto it = m_OutOfSequencePackets.rbegin(); + it++; // prev packet num + while (it != m_OutOfSequencePackets.rend() && *it == ackThrough - acnt - 1) { + acnt++; + it++; + } + // ranges + uint32_t lastNum = ackThrough - acnt; + if (acnt > 255) { + auto d = std::div(acnt - 255, 255); + acnt = 255; + if (d.quot > maxNumRanges) { + d.quot = maxNumRanges; + d.rem = 0; + } + // Acks only ragnes for acnt + for (int i = 0; i < d.quot; i++) { + buf[8 + numRanges * 2] = 0; + buf[8 + numRanges * 2 + 1] = 255; // NACKs 0, Acks 255 + numRanges++; + } + if (d.rem > 0) { + buf[8 + numRanges * 2] = 0; + buf[8 + numRanges * 2 + 1] = d.rem; + numRanges++; + } + } + while (it != m_OutOfSequencePackets.rend() && numRanges < maxNumRanges) { + if (lastNum - (*it) > 255) { + // NACKs only ranges + if (lastNum > (*it) + 255 * (maxNumRanges - numRanges)) break; // too many NACKs + while (lastNum - (*it) > 255) { + buf[8 + numRanges * 2] = 255; + buf[8 + numRanges * 2 + 1] = 0; // NACKs 255, Acks 0 + lastNum -= 255; + numRanges++; + } + } + // NACKs and Acks ranges + buf[8 + numRanges * 2] = lastNum - (*it) - 1; // NACKs + lastNum = *it; + it++; + int numAcks = 1; + while (it != m_OutOfSequencePackets.rend() && lastNum > 0 && *it == lastNum - 1) { + numAcks++; + lastNum--; + it++; + } + while (numAcks > 255) { + // Acks only ranges + buf[8 + numRanges * 2 + 1] = 255; // Acks 255 + numAcks -= 255; + numRanges++; + buf[8 + numRanges * 2] = 0; // NACKs 0 + if (numRanges >= maxNumRanges) break; + } + if (numAcks > 255) numAcks = 255; + buf[8 + numRanges * 2 + 1] = (uint8_t) numAcks; // Acks + numRanges++; + } + if (numRanges < maxNumRanges && it == m_OutOfSequencePackets.rend()) { + // add range between out-of-seqence and received + int nacks = *m_OutOfSequencePackets.begin() - m_ReceivePacketNum - 1; + if (nacks > 0) { + if (nacks > 255) nacks = 255; + buf[8 + numRanges * 2] = nacks; + buf[8 + numRanges * 2 + 1] = std::min((int) m_ReceivePacketNum + 1, 255); + numRanges++; + } + } + } + } + buf[7] = (uint8_t) acnt; // acnt + htobe16buf(buf + 1, 5 + numRanges * 2); + return 8 + numRanges * 2; + } - void SSU2Session::SendQuickAck () - { - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = CreateAckBlock (payload, m_MaxPayloadSize); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - } + size_t SSU2Session::CreatePaddingBlock(uint8_t *buf, size_t len, size_t minSize) { + if (len < minSize) return 0; + uint8_t paddingSize = rand() & 0x0F; // 0 - 15 + if (paddingSize > len) paddingSize = len; + else if (paddingSize < minSize) paddingSize = minSize; + if (paddingSize) { + buf[0] = eSSU2BlkPadding; + htobe16buf(buf + 1, paddingSize); + memset(buf + 3, 0, paddingSize); + } else + return 0; + return paddingSize + 3; + } - void SSU2Session::SendTermination () - { - uint8_t payload[32]; - size_t payloadSize = CreateTerminationBlock (payload, 32); - payloadSize += CreatePaddingBlock (payload + payloadSize, 32 - payloadSize); - SendData (payload, payloadSize); - } + size_t SSU2Session::CreateI2NPBlock(uint8_t *buf, size_t len, std::shared_ptr &&msg) { + msg->ToNTCP2(); + auto msgBuf = msg->GetNTCP2Header(); + auto msgLen = msg->GetNTCP2Length(); + if (msgLen + 3 > len) msgLen = len - 3; + buf[0] = eSSU2BlkI2NPMessage; + htobe16buf(buf + 1, msgLen); // size + memcpy(buf + 3, msgBuf, msgLen); + return msgLen + 3; + } - void SSU2Session::SendPathResponse (const uint8_t * data, size_t len) - { - if (len < 8 || len > m_MaxPayloadSize - 3) - { - LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len); - return; - } - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - payload[0] = eSSU2BlkPathResponse; - htobe16buf (payload + 1, len); - memcpy (payload + 3, data, len); - SendData (payload, len + 3); - } - - void SSU2Session::CleanUp (uint64_t ts) - { - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) - { - if (ts > it->second->lastFragmentInsertTime + SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) - { - LogPrint (eLogWarning, "SSU2: message ", it->first, " was not completed in ", SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); - it = m_IncompleteMessages.erase (it); - } - else - ++it; - } - if (!m_OutOfSequencePackets.empty ()) - { - if (m_OutOfSequencePackets.size () > 2*SSU2_MAX_NUM_ACK_RANGES || - *m_OutOfSequencePackets.rbegin () > m_ReceivePacketNum + 255*8) - { - uint32_t packet = *m_OutOfSequencePackets.begin (); - if (packet > m_ReceivePacketNum + 1) - { - // like we've just received all packets before first - packet--; - m_ReceivePacketNum = packet - 1; - UpdateReceivePacketNum (packet); - } - else - LogPrint (eLogError, "SSU2: Out of sequence packet ", packet, " is less than last received ", m_ReceivePacketNum); - } - if (m_OutOfSequencePackets.size () > 255*4) - { - // seems we have a serious network issue - m_ReceivePacketNum = *m_OutOfSequencePackets.rbegin (); - m_OutOfSequencePackets.clear (); - } - } - - for (auto it = m_RelaySessions.begin (); it != m_RelaySessions.end ();) - { - if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT) - { - LogPrint (eLogWarning, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted"); - it = m_RelaySessions.erase (it); - } - else - ++it; - } - for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) - { - if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT) - { - LogPrint (eLogWarning, "SSU2: Peer test nonce ", it->first, " was not responded in ", SSU2_PEER_TEST_EXPIRATION_TIMEOUT, " seconds, deleted"); - it = m_PeerTests.erase (it); - } - else - ++it; - } - } + size_t SSU2Session::CreateFirstFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr msg) { + if (len < 12) return 0; + msg->ToNTCP2(); + auto msgBuf = msg->GetNTCP2Header(); + auto msgLen = msg->GetNTCP2Length(); + if (msgLen + 3 <= len) return 0; + msgLen = len - 3; + buf[0] = eSSU2BlkFirstFragment; + htobe16buf(buf + 1, msgLen); // size + memcpy(buf + 3, msgBuf, msgLen); + msg->offset = (msgBuf - msg->buf) + msgLen; + return msgLen + 3; + } - void SSU2Session::FlushData () - { - bool sent = SendQueue (); // if we have something to send - if (m_IsDataReceived) - { - if (!sent) SendQuickAck (); - m_Handler.Flush (); - m_IsDataReceived = false; - } - } + size_t SSU2Session::CreateFollowOnFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr msg, + uint8_t &fragmentNum, uint32_t msgID) { + if (len < 8) return 0; + bool isLast = true; + auto msgLen = msg->len - msg->offset; + if (msgLen + 8 > len) { + msgLen = len - 8; + isLast = false; + } + buf[0] = eSSU2BlkFollowOnFragment; + htobe16buf(buf + 1, msgLen + 5); // size + fragmentNum++; + buf[3] = fragmentNum << 1; + if (isLast) buf[3] |= 0x01; + memcpy(buf + 4, &msgID, 4); + memcpy(buf + 8, msg->buf + msg->offset, msgLen); + msg->offset += msgLen; + return msgLen + 8; + } -} + size_t + SSU2Session::CreateRelayIntroBlock(uint8_t *buf, size_t len, const uint8_t *introData, size_t introDataLen) { + buf[0] = eSSU2BlkRelayIntro; + size_t payloadSize = 1/* flag */ + 32/* Alice router hash */ + introDataLen; + if (payloadSize + 3 > len) return 0; + htobe16buf(buf + 1, payloadSize); // size + buf[3] = 0; // flag + memcpy(buf + 4, GetRemoteIdentity()->GetIdentHash(), 32); // Alice router hash + memcpy(buf + 36, introData, introDataLen); + return payloadSize + 3; + } + + size_t SSU2Session::CreateRelayResponseBlock(uint8_t *buf, size_t len, + SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, + bool v4) { + buf[0] = eSSU2BlkRelayResponse; + buf[3] = 0; // flag + buf[4] = code; // code + htobe32buf(buf + 5, nonce); // nonce + htobe32buf(buf + 9, i2p::util::GetSecondsSinceEpoch()); // timestamp + buf[13] = 2; // ver + size_t csz = 0; + if (code == eSSU2RelayResponseCodeAccept) { + auto addr = i2p::context.GetRouterInfo().GetSSU2Address(v4); + if (!addr) { + LogPrint(eLogError, "SSU2: Can't find local address for RelayResponse"); + return 0; + } + csz = CreateEndpoint(buf + 15, len - 15, boost::asio::ip::udp::endpoint(addr->host, addr->port)); + if (!csz) { + LogPrint(eLogError, "SSU2: Can't create local endpoint for RelayResponse"); + return 0; + } + } + buf[14] = csz; // csz + // signature + size_t signatureLen = i2p::context.GetIdentity()->GetSignatureLen(); + if (15 + csz + signatureLen > len) { + LogPrint(eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len); + return 0; + } + SignedData s; + s.Insert((const uint8_t *) "RelayAgreementOK", 16); // prologue + if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + else // Bob's reject + s.Insert(i2p::context.GetIdentity()->GetIdentHash(), 32); // bhash + s.Insert(buf + 5, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint + s.Sign(i2p::context.GetPrivateKeys(), buf + 15 + csz); + size_t payloadSize = 12 + csz + signatureLen; + if (!code) { + if (payloadSize + 11 > len) { + LogPrint(eLogError, "SSU2: Buffer for RelayResponse token is too small ", len); + return 0; + } + memcpy(buf + 3 + payloadSize, &token, 8); + payloadSize += 8; + } + htobe16buf(buf + 1, payloadSize); // size + return payloadSize + 3; + } + + size_t SSU2Session::CreatePeerTestBlock(uint8_t *buf, size_t len, uint8_t msg, SSU2PeerTestCode code, + const uint8_t *routerHash, const uint8_t *signedData, + size_t signedDataLen) { + buf[0] = eSSU2BlkPeerTest; + size_t payloadSize = 3/* msg, code, flag */ + signedDataLen; + if (routerHash) payloadSize += 32; // router hash + if (payloadSize + 3 > len) return 0; + htobe16buf(buf + 1, payloadSize); // size + buf[3] = msg; // msg + buf[4] = (uint8_t) code; // code + buf[5] = 0; //flag + size_t offset = 6; + if (routerHash) { + memcpy(buf + offset, routerHash, 32); // router hash + offset += 32; + } + memcpy(buf + offset, signedData, signedDataLen); + return payloadSize + 3; + } + + size_t SSU2Session::CreatePeerTestBlock(uint8_t *buf, size_t len, uint32_t nonce) { + auto localAddress = FindLocalAddress(); + if (!localAddress || !localAddress->port || localAddress->host.is_unspecified() || + localAddress->host.is_v4() != m_RemoteEndpoint.address().is_v4()) { + LogPrint(eLogWarning, "SSU2: Can't find local address for peer test"); + return 0; + } + // signed data + auto ts = i2p::util::GetSecondsSinceEpoch(); + uint8_t signedData[96]; + signedData[0] = 2; // ver + htobe32buf(signedData + 1, nonce); + htobe32buf(signedData + 5, ts); + size_t asz = CreateEndpoint(signedData + 10, 86, + boost::asio::ip::udp::endpoint(localAddress->host, localAddress->port)); + signedData[9] = asz; + // signature + SignedData s; + s.Insert((const uint8_t *) "PeerTestValidate", 16); // prologue + s.Insert(GetRemoteIdentity()->GetIdentHash(), 32); // bhash + s.Insert(signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint + s.Sign(i2p::context.GetPrivateKeys(), signedData + 10 + asz); + return CreatePeerTestBlock(buf, len, 1, eSSU2PeerTestCodeAccept, nullptr, + signedData, 10 + asz + i2p::context.GetIdentity()->GetSignatureLen()); + } + + size_t SSU2Session::CreateTerminationBlock(uint8_t *buf, size_t len) { + buf[0] = eSSU2BlkTermination; + htobe16buf(buf + 1, 9); + htobe64buf(buf + 3, m_ReceivePacketNum); + buf[11] = (uint8_t) m_TerminationReason; + return 12; + } + + std::shared_ptr SSU2Session::ExtractRouterInfo(const uint8_t *buf, size_t size) { + if (size < 2) return nullptr; + // TODO: handle frag + std::shared_ptr ri; + if (buf[0] & SSU2_ROUTER_INFO_FLAG_GZIP) { + i2p::data::GzipInflator inflator; + uint8_t uncompressed[i2p::data::MAX_RI_BUFFER_SIZE]; + size_t uncompressedSize = inflator.Inflate(buf + 2, size - 2, uncompressed, + i2p::data::MAX_RI_BUFFER_SIZE); + if (uncompressedSize && uncompressedSize < i2p::data::MAX_RI_BUFFER_SIZE) + ri = std::make_shared(uncompressed, uncompressedSize); + else + LogPrint(eLogInfo, "SSU2: RouterInfo decompression failed ", uncompressedSize); + } else + ri = std::make_shared(buf + 2, size - 2); + return ri; + } + + void SSU2Session::CreateNonce(uint64_t seqn, uint8_t *nonce) { + memset(nonce, 0, 4); + htole64buf(nonce + 4, seqn); + } + + bool SSU2Session::UpdateReceivePacketNum(uint32_t packetNum) { + if (packetNum <= m_ReceivePacketNum) return false; // duplicate + if (packetNum == m_ReceivePacketNum + 1) { + for (auto it = m_OutOfSequencePackets.begin(); it != m_OutOfSequencePackets.end();) { + if (*it == packetNum + 1) { + packetNum++; + it = m_OutOfSequencePackets.erase(it); + } else + break; + } + m_ReceivePacketNum = packetNum; + } else + m_OutOfSequencePackets.insert(packetNum); + return true; + } + + void SSU2Session::SendQuickAck() { + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateAckBlock(payload, m_MaxPayloadSize); + payloadSize += CreatePaddingBlock(payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData(payload, payloadSize); + } + + void SSU2Session::SendTermination() { + uint8_t payload[32]; + size_t payloadSize = CreateTerminationBlock(payload, 32); + payloadSize += CreatePaddingBlock(payload + payloadSize, 32 - payloadSize); + SendData(payload, payloadSize); + } + + void SSU2Session::SendPathResponse(const uint8_t *data, size_t len) { + if (len < 8 || len > m_MaxPayloadSize - 3) { + LogPrint(eLogWarning, "SSU2: Incorrect data size for path response ", len); + return; + } + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + payload[0] = eSSU2BlkPathResponse; + htobe16buf(payload + 1, len); + memcpy(payload + 3, data, len); + SendData(payload, len + 3); + } + + void SSU2Session::CleanUp(uint64_t ts) { + for (auto it = m_IncompleteMessages.begin(); it != m_IncompleteMessages.end();) { + if (ts > it->second->lastFragmentInsertTime + SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) { + LogPrint(eLogWarning, "SSU2: message ", it->first, " was not completed in ", + SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); + it = m_IncompleteMessages.erase(it); + } else + ++it; + } + if (!m_OutOfSequencePackets.empty()) { + if (m_OutOfSequencePackets.size() > 2 * SSU2_MAX_NUM_ACK_RANGES || + *m_OutOfSequencePackets.rbegin() > m_ReceivePacketNum + 255 * 8) { + uint32_t packet = *m_OutOfSequencePackets.begin(); + if (packet > m_ReceivePacketNum + 1) { + // like we've just received all packets before first + packet--; + m_ReceivePacketNum = packet - 1; + UpdateReceivePacketNum(packet); + } else + LogPrint(eLogError, "SSU2: Out of sequence packet ", packet, " is less than last received ", + m_ReceivePacketNum); + } + if (m_OutOfSequencePackets.size() > 255 * 4) { + // seems we have a serious network issue + m_ReceivePacketNum = *m_OutOfSequencePackets.rbegin(); + m_OutOfSequencePackets.clear(); + } + } + + for (auto it = m_RelaySessions.begin(); it != m_RelaySessions.end();) { + if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT) { + LogPrint(eLogWarning, "SSU2: Relay nonce ", it->first, " was not responded in ", + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted"); + it = m_RelaySessions.erase(it); + } else + ++it; + } + for (auto it = m_PeerTests.begin(); it != m_PeerTests.end();) { + if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT) { + LogPrint(eLogWarning, "SSU2: Peer test nonce ", it->first, " was not responded in ", + SSU2_PEER_TEST_EXPIRATION_TIMEOUT, " seconds, deleted"); + it = m_PeerTests.erase(it); + } else + ++it; + } + } + + void SSU2Session::FlushData() { + bool sent = SendQueue(); // if we have something to send + if (m_IsDataReceived) { + if (!sent) SendQuickAck(); + m_Handler.Flush(); + m_IsDataReceived = false; + } + } + + } } diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 7f8020f2..8e86e1ed 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -20,340 +20,403 @@ #include "RouterContext.h" #include "TransportSession.h" -namespace i2p -{ -namespace transport -{ - const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds - const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes - const int SSU2_CLOCK_SKEW = 60; // in seconds - const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust - const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds - const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds - const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds - const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds - const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds - const size_t SSU2_MAX_PACKET_SIZE = 1500; - const size_t SSU2_MIN_PACKET_SIZE = 1280; - const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds - const int SSU2_RESEND_INTERVAL = 300; // in milliseconds - const int SSU2_MAX_NUM_RESENDS = 5; - const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds - const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets - const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets - const size_t SSU2_MIN_RTO = 100; // in milliseconds - const size_t SSU2_MAX_RTO = 2500; // in milliseconds - const float SSU2_kAPPA = 1.8; - const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages - const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send - const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; +namespace i2p { + namespace transport { + const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds + const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU2_CLOCK_SKEW = 60; // in seconds + const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust + const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds + const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52 * 60; // for next token block, in seconds + const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds + const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds + const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds + const size_t SSU2_MAX_PACKET_SIZE = 1500; + const size_t SSU2_MIN_PACKET_SIZE = 1280; + const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds + const int SSU2_RESEND_INTERVAL = 300; // in milliseconds + const int SSU2_MAX_NUM_RESENDS = 5; + const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds + const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets + const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets + const size_t SSU2_MIN_RTO = 100; // in milliseconds + const size_t SSU2_MAX_RTO = 2500; // in milliseconds + const float SSU2_kAPPA = 1.8; + const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages + const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send + const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; - enum SSU2MessageType - { - eSSU2SessionRequest = 0, - eSSU2SessionCreated = 1, - eSSU2SessionConfirmed = 2, - eSSU2Data = 6, - eSSU2PeerTest = 7, - eSSU2Retry = 9, - eSSU2TokenRequest = 10, - eSSU2HolePunch = 11 - }; + enum SSU2MessageType { + eSSU2SessionRequest = 0, + eSSU2SessionCreated = 1, + eSSU2SessionConfirmed = 2, + eSSU2Data = 6, + eSSU2PeerTest = 7, + eSSU2Retry = 9, + eSSU2TokenRequest = 10, + eSSU2HolePunch = 11 + }; - enum SSU2BlockType - { - eSSU2BlkDateTime = 0, - eSSU2BlkOptions, // 1 - eSSU2BlkRouterInfo, // 2 - eSSU2BlkI2NPMessage, // 3 - eSSU2BlkFirstFragment, // 4 - eSSU2BlkFollowOnFragment, // 5 - eSSU2BlkTermination, // 6 - eSSU2BlkRelayRequest, // 7 - eSSU2BlkRelayResponse, // 8 - eSSU2BlkRelayIntro, // 9 - eSSU2BlkPeerTest, // 10 - eSSU2BlkNextNonce, // 11 - eSSU2BlkAck, // 12 - eSSU2BlkAddress, // 13 - eSSU2BlkIntroKey, // 14 - eSSU2BlkRelayTagRequest, // 15 - eSSU2BlkRelayTag, // 16 - eSSU2BlkNewToken, // 17 - eSSU2BlkPathChallenge, // 18 - eSSU2BlkPathResponse, // 19 - eSSU2BlkFirstPacketNumber, // 20 - eSSU2BlkPadding = 254 - }; + enum SSU2BlockType { + eSSU2BlkDateTime = 0, + eSSU2BlkOptions, // 1 + eSSU2BlkRouterInfo, // 2 + eSSU2BlkI2NPMessage, // 3 + eSSU2BlkFirstFragment, // 4 + eSSU2BlkFollowOnFragment, // 5 + eSSU2BlkTermination, // 6 + eSSU2BlkRelayRequest, // 7 + eSSU2BlkRelayResponse, // 8 + eSSU2BlkRelayIntro, // 9 + eSSU2BlkPeerTest, // 10 + eSSU2BlkNextNonce, // 11 + eSSU2BlkAck, // 12 + eSSU2BlkAddress, // 13 + eSSU2BlkIntroKey, // 14 + eSSU2BlkRelayTagRequest, // 15 + eSSU2BlkRelayTag, // 16 + eSSU2BlkNewToken, // 17 + eSSU2BlkPathChallenge, // 18 + eSSU2BlkPathResponse, // 19 + eSSU2BlkFirstPacketNumber, // 20 + eSSU2BlkPadding = 254 + }; - enum SSU2SessionState - { - eSSU2SessionStateUnknown, - eSSU2SessionStateTokenReceived, - eSSU2SessionStateSessionRequestSent, - eSSU2SessionStateSessionRequestReceived, - eSSU2SessionStateSessionCreatedSent, - eSSU2SessionStateSessionCreatedReceived, - eSSU2SessionStateSessionConfirmedSent, - eSSU2SessionStateEstablished, - eSSU2SessionStateClosing, - eSSU2SessionStateTerminated, - eSSU2SessionStateFailed, - eSSU2SessionStateIntroduced, - eSSU2SessionStatePeerTest, - eSSU2SessionStatePeerTestReceived, // 5 before 4 - eSSU2SessionStateTokenRequestReceived - }; + enum SSU2SessionState { + eSSU2SessionStateUnknown, + eSSU2SessionStateTokenReceived, + eSSU2SessionStateSessionRequestSent, + eSSU2SessionStateSessionRequestReceived, + eSSU2SessionStateSessionCreatedSent, + eSSU2SessionStateSessionCreatedReceived, + eSSU2SessionStateSessionConfirmedSent, + eSSU2SessionStateEstablished, + eSSU2SessionStateClosing, + eSSU2SessionStateTerminated, + eSSU2SessionStateFailed, + eSSU2SessionStateIntroduced, + eSSU2SessionStatePeerTest, + eSSU2SessionStatePeerTestReceived, // 5 before 4 + eSSU2SessionStateTokenRequestReceived + }; - enum SSU2PeerTestCode - { - eSSU2PeerTestCodeAccept = 0, - eSSU2PeerTestCodeBobReasonUnspecified = 1, - eSSU2PeerTestCodeBobNoCharlieAvailable = 2, - eSSU2PeerTestCodeBobLimitExceeded = 3, - eSSU2PeerTestCodeBobSignatureFailure = 4, - eSSU2PeerTestCodeCharlieReasonUnspecified = 64, - eSSU2PeerTestCodeCharlieUnsupportedAddress = 65, - eSSU2PeerTestCodeCharlieLimitExceeded = 66, - eSSU2PeerTestCodeCharlieSignatureFailure = 67, - eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68, - eSSU2PeerTestCodeCharlieAliceIsBanned = 69, - eSSU2PeerTestCodeCharlieAliceIsUnknown = 70, - eSSU2PeerTestCodeUnspecified = 128 - }; + enum SSU2PeerTestCode { + eSSU2PeerTestCodeAccept = 0, + eSSU2PeerTestCodeBobReasonUnspecified = 1, + eSSU2PeerTestCodeBobNoCharlieAvailable = 2, + eSSU2PeerTestCodeBobLimitExceeded = 3, + eSSU2PeerTestCodeBobSignatureFailure = 4, + eSSU2PeerTestCodeCharlieReasonUnspecified = 64, + eSSU2PeerTestCodeCharlieUnsupportedAddress = 65, + eSSU2PeerTestCodeCharlieLimitExceeded = 66, + eSSU2PeerTestCodeCharlieSignatureFailure = 67, + eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68, + eSSU2PeerTestCodeCharlieAliceIsBanned = 69, + eSSU2PeerTestCodeCharlieAliceIsUnknown = 70, + eSSU2PeerTestCodeUnspecified = 128 + }; - enum SSU2RelayResponseCode - { - eSSU2RelayResponseCodeAccept = 0, - eSSU2RelayResponseCodeBobRelayTagNotFound = 5, - eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65, - eSSU2RelayResponseCodeCharlieSignatureFailure = 67, - eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 - }; + enum SSU2RelayResponseCode { + eSSU2RelayResponseCodeAccept = 0, + eSSU2RelayResponseCodeBobRelayTagNotFound = 5, + eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65, + eSSU2RelayResponseCodeCharlieSignatureFailure = 67, + eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 + }; - enum SSU2TerminationReason - { - eSSU2TerminationReasonNormalClose = 0, - eSSU2TerminationReasonTerminationReceived = 1, - eSSU2TerminationReasonIdleTimeout = 2, - eSSU2TerminationReasonRouterShutdown = 3, - eSSU2TerminationReasonDataPhaseAEADFailure= 4, - eSSU2TerminationReasonIncompatibleOptions = 5, - eSSU2TerminationReasonTncompatibleSignatureType = 6, - eSSU2TerminationReasonClockSkew = 7, - eSSU2TerminationPaddingViolation = 8, - eSSU2TerminationReasonAEADFramingError = 9, - eSSU2TerminationReasonPayloadFormatError = 10, - eSSU2TerminationReasonSessionRequestError = 11, - eSSU2TerminationReasonSessionCreatedError = 12, - eSSU2TerminationReasonSessionConfirmedError = 13, - eSSU2TerminationReasonTimeout = 14, - eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15, - eSSU2TerminationReasonInvalidS = 16, - eSSU2TerminationReasonBanned = 17, - eSSU2TerminationReasonBadToken = 18, - eSSU2TerminationReasonConnectionLimits = 19, - eSSU2TerminationReasonIncompatibleVersion = 20, - eSSU2TerminationReasonWrongNetID = 21, - eSSU2TerminationReasonReplacedByNewSession = 22 - }; - - struct SSU2IncompleteMessage - { - struct Fragment - { - uint8_t buf[SSU2_MAX_PACKET_SIZE]; - size_t len; - bool isLast; - }; + enum SSU2TerminationReason { + eSSU2TerminationReasonNormalClose = 0, + eSSU2TerminationReasonTerminationReceived = 1, + eSSU2TerminationReasonIdleTimeout = 2, + eSSU2TerminationReasonRouterShutdown = 3, + eSSU2TerminationReasonDataPhaseAEADFailure = 4, + eSSU2TerminationReasonIncompatibleOptions = 5, + eSSU2TerminationReasonTncompatibleSignatureType = 6, + eSSU2TerminationReasonClockSkew = 7, + eSSU2TerminationPaddingViolation = 8, + eSSU2TerminationReasonAEADFramingError = 9, + eSSU2TerminationReasonPayloadFormatError = 10, + eSSU2TerminationReasonSessionRequestError = 11, + eSSU2TerminationReasonSessionCreatedError = 12, + eSSU2TerminationReasonSessionConfirmedError = 13, + eSSU2TerminationReasonTimeout = 14, + eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15, + eSSU2TerminationReasonInvalidS = 16, + eSSU2TerminationReasonBanned = 17, + eSSU2TerminationReasonBadToken = 18, + eSSU2TerminationReasonConnectionLimits = 19, + eSSU2TerminationReasonIncompatibleVersion = 20, + eSSU2TerminationReasonWrongNetID = 21, + eSSU2TerminationReasonReplacedByNewSession = 22 + }; - std::shared_ptr msg; - int nextFragmentNum; - uint32_t lastFragmentInsertTime; // in seconds - std::map > outOfSequenceFragments; + struct SSU2IncompleteMessage { + struct Fragment { + uint8_t buf[SSU2_MAX_PACKET_SIZE]; + size_t len; + bool isLast; + }; - void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); - }; + std::shared_ptr msg; + int nextFragmentNum; + uint32_t lastFragmentInsertTime; // in seconds + std::map > outOfSequenceFragments; - struct SSU2SentPacket - { - 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; + void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize); + }; - class SSU2Server; - class SSU2Session: public TransportSession, public std::enable_shared_from_this - { - 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; - }; + struct SSU2SentPacket { + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = 0; + uint64_t sendTime; // in milliseconds + int numResends = 0; + }; - struct HandshakePacket - { - Header header; - 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 - }; + // RouterInfo flags + const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; + const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02; - typedef std::function OnEstablished; + class SSU2Server; - public: + class SSU2Session : public TransportSession, public std::enable_shared_from_this { + 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 in_RemoteRouter = nullptr, - std::shared_ptr addr = nullptr); - ~SSU2Session (); + struct HandshakePacket { + Header header; + 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; }; - const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; - i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; }; - std::shared_ptr GetAddress () const { return m_Address; }; - void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; - OnEstablished GetOnEstablished () const { return m_OnEstablished; }; + typedef std::function OnEstablished; - void Connect (); - bool Introduce (std::shared_ptr 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 >& 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; }; + public: - 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); + SSU2Session(SSU2Server &server, std::shared_ptr in_RemoteRouter = nullptr, + std::shared_ptr addr = nullptr); - private: + ~SSU2Session(); - void Terminate (); - void Established (); - void ScheduleConnectTimer (); - void HandleConnectTimer (const boost::system::error_code& ecode); - void PostI2NPMessages (std::vector > msgs); - bool SendQueue (); // returns true if ack block was sent - bool SendFragmentedMessage (std::shared_ptr 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 SetRemoteEndpoint(const boost::asio::ip::udp::endpoint &ep) { m_RemoteEndpoint = ep; }; - 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 FindLocalAddress () const; - void AdjustMaxPayloadSize (); - RouterStatus GetRouterStatus () const; - void SetRouterStatus (RouterStatus status) const; - std::shared_ptr 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 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); + const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() const { return m_RemoteEndpoint; }; - 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 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&& msg); - size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg); - size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr 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); + i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports() const { return m_RemoteTransports; }; - private: + std::shared_ptr GetAddress() const { return m_Address; }; - SSU2Server& m_Server; - std::shared_ptr m_EphemeralKeys; - std::unique_ptr m_NoiseState; - std::unique_ptr m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice - std::unique_ptr m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed - std::shared_ptr 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 m_OutOfSequencePackets; // packet nums > receive packet num - std::map > m_SentPackets; // packetNum -> packet - std::map > m_IncompleteMessages; // I2NP - std::map, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice - std::map, uint64_t > > m_PeerTests; // same as for relay sessions - std::list > 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; - }; + void SetOnEstablished(OnEstablished e) { m_OnEstablished = e; }; - 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; - } -} + OnEstablished GetOnEstablished() const { return m_OnEstablished; }; + + void Connect(); + + bool Introduce(std::shared_ptr 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 > &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 > msgs); + + bool SendQueue(); // returns true if ack block was sent + bool SendFragmentedMessage(std::shared_ptr 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 FindLocalAddress() const; + + void AdjustMaxPayloadSize(); + + RouterStatus GetRouterStatus() const; + + void SetRouterStatus(RouterStatus status) const; + + std::shared_ptr 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 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 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 &&msg); + + size_t CreateFirstFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr msg); + + size_t CreateFollowOnFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr 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 m_EphemeralKeys; + std::unique_ptr m_NoiseState; + std::unique_ptr m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice + std::unique_ptr m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed + std::shared_ptr 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 m_OutOfSequencePackets; // packet nums > receive packet num + std::map > m_SentPackets; // packetNum -> packet + std::map > m_IncompleteMessages; // I2NP + std::map, uint64_t> > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice + std::map, uint64_t> > m_PeerTests; // same as for relay sessions + std::list > 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 diff --git a/libi2pd/SSUData.cpp b/libi2pd/SSUData.cpp index 6365381e..3fc9f2d3 100644 --- a/libi2pd/SSUData.cpp +++ b/libi2pd/SSUData.cpp @@ -13,504 +13,436 @@ #include "SSU.h" #include "SSUData.h" -namespace i2p -{ -namespace transport -{ - void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) - { - if (msg->len + fragmentSize > msg->maxLen) - { - LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (); - *newMsg = *msg; - msg = newMsg; - } - if (msg->Concat (fragment, fragmentSize) < fragmentSize) - LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen); - nextFragmentNum++; - } +namespace i2p { + namespace transport { + void IncompleteMessage::AttachNextFragment(const uint8_t *fragment, size_t fragmentSize) { + if (msg->len + fragmentSize > msg->maxLen) { + LogPrint(eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough"); + auto newMsg = NewI2NPMessage(); + *newMsg = *msg; + msg = newMsg; + } + if (msg->Concat(fragment, fragmentSize) < fragmentSize) + LogPrint(eLogError, "SSU: I2NP buffer overflow ", msg->maxLen); + nextFragmentNum++; + } - SSUData::SSUData (SSUSession& session): - m_Session (session), m_ResendTimer (session.GetService ()), - m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), - m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) - { - } + SSUData::SSUData(SSUSession &session) : + m_Session(session), m_ResendTimer(session.GetService()), + m_MaxPacketSize(session.IsV6() ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), + m_PacketSize(m_MaxPacketSize), m_LastMessageReceivedTime(0) { + } - SSUData::~SSUData () - { - } + SSUData::~SSUData() { + } - void SSUData::Start () - { - } + void SSUData::Start() { + } - void SSUData::Stop () - { - m_ResendTimer.cancel (); - m_IncompleteMessages.clear (); - m_SentMessages.clear (); - m_ReceivedMessages.clear (); - } + void SSUData::Stop() { + m_ResendTimer.cancel(); + m_IncompleteMessages.clear(); + m_SentMessages.clear(); + m_ReceivedMessages.clear(); + } - void SSUData::AdjustPacketSize (std::shared_ptr remoteRouter) - { - if (!remoteRouter) return; - auto ssuAddress = remoteRouter->GetSSUAddress (); - if (ssuAddress && ssuAddress->ssu->mtu) - { - if (m_Session.IsV6 ()) - m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; - else - m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; - if (m_PacketSize > 0) - { - // make sure packet size multiple of 16 - m_PacketSize >>= 4; - m_PacketSize <<= 4; - if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; - LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize); - } - else - { - LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu); - m_PacketSize = m_MaxPacketSize; - } - } - } + void SSUData::AdjustPacketSize(std::shared_ptr remoteRouter) { + if (!remoteRouter) return; + auto ssuAddress = remoteRouter->GetSSUAddress(); + if (ssuAddress && ssuAddress->ssu->mtu) { + if (m_Session.IsV6()) + m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; + else + m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; + if (m_PacketSize > 0) { + // make sure packet size multiple of 16 + m_PacketSize >>= 4; + m_PacketSize <<= 4; + if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; + LogPrint(eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize); + } else { + LogPrint(eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu); + m_PacketSize = m_MaxPacketSize; + } + } + } - void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) - { - auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); - if (routerInfo) - AdjustPacketSize (routerInfo); - } + void SSUData::UpdatePacketSize(const i2p::data::IdentHash &remoteIdent) { + auto routerInfo = i2p::data::netdb.FindRouter(remoteIdent); + if (routerInfo) + AdjustPacketSize(routerInfo); + } - void SSUData::ProcessSentMessageAck (uint32_t msgID) - { - auto it = m_SentMessages.find (msgID); - if (it != m_SentMessages.end ()) - { - m_SentMessages.erase (it); - if (m_SentMessages.empty ()) - m_ResendTimer.cancel (); - } - } + void SSUData::ProcessSentMessageAck(uint32_t msgID) { + auto it = m_SentMessages.find(msgID); + if (it != m_SentMessages.end()) { + m_SentMessages.erase(it); + if (m_SentMessages.empty()) + m_ResendTimer.cancel(); + } + } - void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) - { - if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) - { - // explicit ACKs - uint8_t numAcks =*buf; - buf++; - for (int i = 0; i < numAcks; i++) - ProcessSentMessageAck (bufbe32toh (buf+i*4)); - buf += numAcks*4; - } - if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) - { - // explicit ACK bitfields - uint8_t numBitfields =*buf; - buf++; - for (int i = 0; i < numBitfields; i++) - { - uint32_t msgID = bufbe32toh (buf); - buf += 4; // msgID - auto it = m_SentMessages.find (msgID); - // process individual Ack bitfields - bool isNonLast = false; - int fragment = 0; - do - { - uint8_t bitfield = *buf; - isNonLast = bitfield & 0x80; - bitfield &= 0x7F; // clear MSB - if (bitfield && it != m_SentMessages.end ()) - { - int numSentFragments = it->second->fragments.size (); - // process bits - uint8_t mask = 0x01; - for (int j = 0; j < 7; j++) - { - if (bitfield & mask) - { - if (fragment < numSentFragments) - it->second->fragments[fragment] = nullptr; - } - fragment++; - mask <<= 1; - } - } - buf++; - } - while (isNonLast); - } - } - } + void SSUData::ProcessAcks(uint8_t *&buf, uint8_t flag) { + if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) { + // explicit ACKs + uint8_t numAcks = *buf; + buf++; + for (int i = 0; i < numAcks; i++) + ProcessSentMessageAck(bufbe32toh(buf + i * 4)); + buf += numAcks * 4; + } + if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) { + // explicit ACK bitfields + uint8_t numBitfields = *buf; + buf++; + for (int i = 0; i < numBitfields; i++) { + uint32_t msgID = bufbe32toh(buf); + buf += 4; // msgID + auto it = m_SentMessages.find(msgID); + // process individual Ack bitfields + bool isNonLast = false; + int fragment = 0; + do { + uint8_t bitfield = *buf; + isNonLast = bitfield & 0x80; + bitfield &= 0x7F; // clear MSB + if (bitfield && it != m_SentMessages.end()) { + int numSentFragments = it->second->fragments.size(); + // process bits + uint8_t mask = 0x01; + for (int j = 0; j < 7; j++) { + if (bitfield & mask) { + if (fragment < numSentFragments) + it->second->fragments[fragment] = nullptr; + } + fragment++; + mask <<= 1; + } + } + buf++; + } while (isNonLast); + } + } + } - void SSUData::ProcessFragments (uint8_t * buf) - { - uint8_t numFragments = *buf; // number of fragments - buf++; - for (int i = 0; i < numFragments; i++) - { - uint32_t msgID = bufbe32toh (buf); // message ID - buf += 4; - uint8_t frag[4] = {0}; - memcpy (frag + 1, buf, 3); - buf += 3; - uint32_t fragmentInfo = bufbe32toh (frag); // fragment info - uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13 - bool isLast = fragmentInfo & 0x010000; // bit 16 - uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 - if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) - { - LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size"); - return; - } + void SSUData::ProcessFragments(uint8_t *buf) { + uint8_t numFragments = *buf; // number of fragments + buf++; + for (int i = 0; i < numFragments; i++) { + uint32_t msgID = bufbe32toh(buf); // message ID + buf += 4; + uint8_t frag[4] = {0}; + memcpy(frag + 1, buf, 3); + buf += 3; + uint32_t fragmentInfo = bufbe32toh(frag); // fragment info + uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13 + bool isLast = fragmentInfo & 0x010000; // bit 16 + uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 + if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) { + LogPrint(eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size"); + return; + } - // find message with msgID - auto it = m_IncompleteMessages.find (msgID); - if (it == m_IncompleteMessages.end ()) - { - // create new message - auto msg = NewI2NPShortMessage (); - msg->len -= I2NP_SHORT_HEADER_SIZE; - it = m_IncompleteMessages.insert (std::make_pair (msgID, - m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first; - } - auto& incompleteMessage = it->second; - // mark fragment as received - if (fragmentNum < 64) - incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum); - else - LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); + // find message with msgID + auto it = m_IncompleteMessages.find(msgID); + if (it == m_IncompleteMessages.end()) { + // create new message + auto msg = NewI2NPShortMessage(); + msg->len -= I2NP_SHORT_HEADER_SIZE; + it = m_IncompleteMessages.insert(std::make_pair(msgID, + m_Session.GetServer().GetIncompleteMessagesPool().AcquireShared( + std::move(msg)))).first; + } + auto &incompleteMessage = it->second; + // mark fragment as received + if (fragmentNum < 64) + incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum); + else + LogPrint(eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); - // handle current fragment - if (fragmentNum == incompleteMessage->nextFragmentNum) - { - // expected fragment - incompleteMessage->AttachNextFragment (buf, fragmentSize); - if (!isLast && !incompleteMessage->savedFragments.empty ()) - { - // try saved fragments - for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) - { - auto& savedFragment = *it1; - if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) - { - incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len); - isLast = savedFragment->isLast; - incompleteMessage->savedFragments.erase (it1++); - } - else - break; - } - if (isLast) - LogPrint (eLogDebug, "SSU: Message ", msgID, " complete"); - } - } - else - { - if (fragmentNum < incompleteMessage->nextFragmentNum) - // duplicate fragment - LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored"); - else - { - // missing fragment - LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); - auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast); - if (incompleteMessage->savedFragments.insert (savedFragment).second) - incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - else - LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); - } - isLast = false; - } + // handle current fragment + if (fragmentNum == incompleteMessage->nextFragmentNum) { + // expected fragment + incompleteMessage->AttachNextFragment(buf, fragmentSize); + if (!isLast && !incompleteMessage->savedFragments.empty()) { + // try saved fragments + for (auto it1 = incompleteMessage->savedFragments.begin(); + it1 != incompleteMessage->savedFragments.end();) { + auto &savedFragment = *it1; + if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) { + incompleteMessage->AttachNextFragment(savedFragment->buf, savedFragment->len); + isLast = savedFragment->isLast; + incompleteMessage->savedFragments.erase(it1++); + } else + break; + } + if (isLast) + LogPrint(eLogDebug, "SSU: Message ", msgID, " complete"); + } + } else { + if (fragmentNum < incompleteMessage->nextFragmentNum) + // duplicate fragment + LogPrint(eLogWarning, "SSU: Duplicate fragment ", (int) fragmentNum, " of message ", msgID, + ", ignored"); + else { + // missing fragment + LogPrint(eLogWarning, "SSU: Missing fragments from ", (int) incompleteMessage->nextFragmentNum, + " to ", fragmentNum - 1, " of message ", msgID); + auto savedFragment = m_Session.GetServer().GetFragmentsPool().AcquireShared(fragmentNum, buf, + fragmentSize, + isLast); + if (incompleteMessage->savedFragments.insert(savedFragment).second) + incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch(); + else + LogPrint(eLogWarning, "SSU: Fragment ", (int) fragmentNum, " of message ", msgID, + " already saved"); + } + isLast = false; + } - if (isLast) - { - // delete incomplete message - auto msg = incompleteMessage->msg; - incompleteMessage->msg = nullptr; - m_IncompleteMessages.erase (msgID); - // process message - SendMsgAck (msgID); - msg->FromSSU (msgID); - if (m_Session.GetState () == eSessionStateEstablished) - { - if (!m_ReceivedMessages.count (msgID)) - { - m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); - m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime); - if (!msg->IsExpired ()) - { - m_Handler.PutNextMessage (std::move (msg)); - } - else - LogPrint (eLogDebug, "SSU: message expired"); - } - else - LogPrint (eLogWarning, "SSU: Message ", msgID, " already received"); - } - else - { - // we expect DeliveryStatus - if (msg->GetTypeID () == eI2NPDeliveryStatus) - { - LogPrint (eLogDebug, "SSU: session established"); - m_Session.Established (); - } - else - LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ()); - } - } - else - SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits); - buf += fragmentSize; - } - } + if (isLast) { + // delete incomplete message + auto msg = incompleteMessage->msg; + incompleteMessage->msg = nullptr; + m_IncompleteMessages.erase(msgID); + // process message + SendMsgAck(msgID); + msg->FromSSU(msgID); + if (m_Session.GetState() == eSessionStateEstablished) { + if (!m_ReceivedMessages.count(msgID)) { + m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch(); + m_ReceivedMessages.emplace(msgID, m_LastMessageReceivedTime); + if (!msg->IsExpired()) { + m_Handler.PutNextMessage(std::move(msg)); + } else + LogPrint(eLogDebug, "SSU: message expired"); + } else + LogPrint(eLogWarning, "SSU: Message ", msgID, " already received"); + } else { + // we expect DeliveryStatus + if (msg->GetTypeID() == eI2NPDeliveryStatus) { + 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 () - { - m_Handler.Flush (); - } + void SSUData::FlushReceivedMessage() { + m_Handler.Flush(); + } - void SSUData::ProcessMessage (uint8_t * buf, size_t len) - { - //uint8_t * start = buf; - uint8_t flag = *buf; - buf++; - LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len); - // process acks if presented - if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) - ProcessAcks (buf, flag); - // extended data if presented - if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) - { - uint8_t extendedDataSize = *buf; - buf++; // size - LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present"); - buf += extendedDataSize; - } - // process data - ProcessFragments (buf); - } + void SSUData::ProcessMessage(uint8_t *buf, size_t len) { + //uint8_t * start = buf; + uint8_t flag = *buf; + buf++; + LogPrint(eLogDebug, "SSU: Process data, flags=", (int) flag, ", len=", len); + // process acks if presented + if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) + ProcessAcks(buf, flag); + // extended data if presented + if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) { + uint8_t extendedDataSize = *buf; + buf++; // size + LogPrint(eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present"); + buf += extendedDataSize; + } + // process data + ProcessFragments(buf); + } - void SSUData::Send (std::shared_ptr msg) - { - uint32_t msgID = msg->ToSSU (); - if (m_SentMessages.find (msgID) != m_SentMessages.end()) - { - LogPrint (eLogWarning, "SSU: message ", msgID, " already sent"); - return; - } - if (m_SentMessages.empty ()) // schedule resend at first message only - ScheduleResend (); + void SSUData::Send(std::shared_ptr msg) { + uint32_t msgID = msg->ToSSU(); + if (m_SentMessages.find(msgID) != m_SentMessages.end()) { + LogPrint(eLogWarning, "SSU: message ", msgID, " already sent"); + return; + } + if (m_SentMessages.empty()) // schedule resend at first message only + ScheduleResend(); - auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ()); - auto& sentMessage = ret.first->second; - if (ret.second) - { - sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; - sentMessage->numResends = 0; - } - auto& fragments = sentMessage->fragments; - size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) - size_t len = msg->GetLength (); - uint8_t * msgBuf = msg->GetSSUHeader (); + auto ret = m_SentMessages.emplace(msgID, m_Session.GetServer().GetSentMessagesPool().AcquireShared()); + auto &sentMessage = ret.first->second; + if (ret.second) { + sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch() + RESEND_INTERVAL; + sentMessage->numResends = 0; + } + auto &fragments = sentMessage->fragments; + size_t payloadSize = + m_PacketSize - sizeof(SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) + size_t len = msg->GetLength(); + uint8_t *msgBuf = msg->GetSSUHeader(); - uint32_t fragmentNum = 0; - while (len > 0 && fragmentNum <= 127) - { - auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (); - fragment->fragmentNum = fragmentNum; - uint8_t * payload = fragment->buf + sizeof (SSUHeader); - *payload = DATA_FLAG_WANT_REPLY; // for compatibility - payload++; - *payload = 1; // always 1 message fragment per message - payload++; - htobe32buf (payload, msgID); - payload += 4; - bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max - size_t size = isLast ? len : payloadSize; - uint32_t fragmentInfo = (fragmentNum << 17); - if (isLast) - fragmentInfo |= 0x010000; + uint32_t fragmentNum = 0; + while (len > 0 && fragmentNum <= 127) { + auto fragment = m_Session.GetServer().GetFragmentsPool().AcquireShared(); + fragment->fragmentNum = fragmentNum; + uint8_t *payload = fragment->buf + sizeof(SSUHeader); + *payload = DATA_FLAG_WANT_REPLY; // for compatibility + payload++; + *payload = 1; // always 1 message fragment per message + payload++; + htobe32buf(payload, msgID); + payload += 4; + bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max + size_t size = isLast ? len : payloadSize; + uint32_t fragmentInfo = (fragmentNum << 17); + if (isLast) + fragmentInfo |= 0x010000; - fragmentInfo |= size; - fragmentInfo = htobe32 (fragmentInfo); - memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); - payload += 3; - memcpy (payload, msgBuf, size); + fragmentInfo |= size; + fragmentInfo = htobe32(fragmentInfo); + memcpy(payload, (uint8_t * )(&fragmentInfo) + 1, 3); + payload += 3; + memcpy(payload, msgBuf, size); - size += payload - fragment->buf; - uint8_t rem = size & 0x0F; - if (rem) // make sure 16 bytes boundary - { - auto padding = 16 - rem; - memset (fragment->buf + size, 0, padding); - size += padding; - } - fragment->len = size; - fragments.push_back (fragment); + size += payload - fragment->buf; + uint8_t rem = size & 0x0F; + if (rem) // make sure 16 bytes boundary + { + auto padding = 16 - rem; + memset(fragment->buf + size, 0, padding); + size += padding; + } + fragment->len = size; + fragments.push_back(fragment); - // encrypt message with session key - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf); - try - { - m_Session.Send (buf, size); - } - catch (boost::system::system_error& ec) - { - LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ()); - } - if (!isLast) - { - len -= payloadSize; - msgBuf += payloadSize; - } - else - len = 0; - fragmentNum++; - } - } + // encrypt message with session key + uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; + m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, fragment->buf, size, buf); + try { + m_Session.Send(buf, size); + } + catch (boost::system::system_error &ec) { + LogPrint(eLogWarning, "SSU: Can't send data fragment ", ec.what()); + } + if (!isLast) { + len -= payloadSize; + msgBuf += payloadSize; + } else + len = 0; + fragmentNum++; + } + } - 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 * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACKs - payload++; - htobe32buf (payload, msgID); // msgID - payload += 4; - *payload = 0; // number of fragments + 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 *payload = buf + sizeof(SSUHeader); + *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACKs + payload++; + htobe32buf(payload, msgID); // msgID + payload += 4; + *payload = 0; // number of fragments - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); - m_Session.Send (buf, 48); - } + // encrypt message with session key + m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, 48); + m_Session.Send(buf, 48); + } - void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits) - { - if (!bits) return; - uint8_t buf[64 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACK bitfields - payload++; - // one ack - *(uint32_t *)(payload) = htobe32 (msgID); // msgID - payload += 4; - size_t len = 0; - while (bits) - { - *payload = (bits & 0x7F); // next 7 bits - bits >>= 7; - if (bits) *payload &= 0x80; // 0x80 means non-last - payload++; len++; - } - *payload = 0; // number of fragments - len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4 - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); - m_Session.Send (buf, len); - } + void SSUData::SendFragmentAck(uint32_t msgID, uint64_t bits) { + if (!bits) return; + uint8_t buf[64 + 18] = {0}; + uint8_t *payload = buf + sizeof(SSUHeader); + *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACK bitfields + payload++; + // one ack + *(uint32_t * )(payload) = htobe32(msgID); // msgID + payload += 4; + size_t len = 0; + while (bits) { + *payload = (bits & 0x7F); // next 7 bits + bits >>= 7; + if (bits) *payload &= 0x80; // 0x80 means non-last + payload++; + len++; + } + *payload = 0; // number of fragments + len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4 + // encrypt message with session key + m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, len); + m_Session.Send(buf, len); + } - void SSUData::ScheduleResend() - { - m_ResendTimer.cancel (); - m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL)); - auto s = m_Session.shared_from_this(); - m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { s->m_Data.HandleResendTimer (ecode); }); - } + void SSUData::ScheduleResend() { + m_ResendTimer.cancel(); + m_ResendTimer.expires_from_now(boost::posix_time::seconds(RESEND_INTERVAL)); + auto s = m_Session.shared_from_this(); + m_ResendTimer.async_wait( + [s](const boost::system::error_code &ecode) { s->m_Data.HandleResendTimer(ecode); }); + } - void SSUData::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numResent = 0; - for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) - { - if (ts >= it->second->nextResendTime) - { - if (it->second->numResends < MAX_NUM_RESENDS) - { - for (auto& f: it->second->fragments) - if (f) - { - try - { - 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 ()); - } - } + void SSUData::HandleResendTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + int numResent = 0; + for (auto it = m_SentMessages.begin(); it != m_SentMessages.end();) { + if (ts >= it->second->nextResendTime) { + if (it->second->numResends < MAX_NUM_RESENDS) { + for (auto &f: it->second->fragments) + if (f) { + try { + 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->nextResendTime += it->second->numResends*RESEND_INTERVAL; - ++it; - } - else - { - LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted"); - it = m_SentMessages.erase (it); - } - } - else - ++it; - } - if (m_SentMessages.empty ()) return; // nothing to resend - if (numResent < MAX_OUTGOING_WINDOW_SIZE) - ScheduleResend (); - else - { - LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated"); - m_Session.Close (); - } - } - } + it->second->numResends++; + it->second->nextResendTime += it->second->numResends * RESEND_INTERVAL; + ++it; + } else { + LogPrint(eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", + MAX_NUM_RESENDS, " attempts, deleted"); + it = m_SentMessages.erase(it); + } + } else + ++it; + } + if (m_SentMessages.empty()) return; // nothing to resend + if (numResent < MAX_OUTGOING_WINDOW_SIZE) + ScheduleResend(); + else { + LogPrint(eLogError, "SSU: resend window exceeds max size. Session terminated"); + m_Session.Close(); + } + } + } - void SSUData::CleanUp (uint64_t ts) - { - 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 ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); - it = m_IncompleteMessages.erase (it); - } - else - ++it; - } + void SSUData::CleanUp(uint64_t ts) { + 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 ", + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); + it = m_IncompleteMessages.erase(it); + } else + ++it; + } - if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL) - // decay - m_ReceivedMessages.clear (); - else - { - // delete old received messages - for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();) - { - if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT) - it = m_ReceivedMessages.erase (it); - else - ++it; - } - } - } -} + if (m_ReceivedMessages.size() > MAX_NUM_RECEIVED_MESSAGES || + ts > m_LastMessageReceivedTime + DECAY_INTERVAL) + // decay + m_ReceivedMessages.clear(); + else { + // delete old received messages + for (auto it = m_ReceivedMessages.begin(); it != m_ReceivedMessages.end();) { + if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT) + it = m_ReceivedMessages.erase(it); + else + ++it; + } + } + } + } } diff --git a/libi2pd/SSUData.h b/libi2pd/SSUData.h index eba0fc28..2100f3e3 100644 --- a/libi2pd/SSUData.h +++ b/libi2pd/SSUData.h @@ -21,111 +21,118 @@ #include "RouterInfo.h" #include "TransportSession.h" -namespace i2p -{ -namespace transport -{ - const size_t SSU_MTU_V4 = 1484; - const size_t SSU_MTU_V6 = 1488; - const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 - const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 - const int RESEND_INTERVAL = 3; // in seconds - const int MAX_NUM_RESENDS = 5; - const int DECAY_INTERVAL = 20; // in seconds - const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds - const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds - const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check - const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store - // data flags - const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; - const uint8_t DATA_FLAG_WANT_REPLY = 0x04; - const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; - const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; - const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; - const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; +namespace i2p { + namespace transport { + const size_t SSU_MTU_V4 = 1484; + const size_t SSU_MTU_V6 = 1488; + const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 + const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 + const int RESEND_INTERVAL = 3; // in seconds + const int MAX_NUM_RESENDS = 5; + const int DECAY_INTERVAL = 20; // in seconds + const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds + const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds + const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check + const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store + // data flags + const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; + const uint8_t DATA_FLAG_WANT_REPLY = 0x04; + const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; + const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; + const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; + const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; - struct Fragment - { - int fragmentNum; - size_t len; - bool isLast; - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest + struct Fragment { + int fragmentNum; + size_t len; + bool isLast; + uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest - Fragment () = default; - Fragment (int n, const uint8_t * b, int l, bool last): - fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); }; - }; + Fragment() = default; - struct FragmentCmp - { - bool operator() (const std::shared_ptr& f1, const std::shared_ptr& f2) const - { - return f1->fragmentNum < f2->fragmentNum; - }; - }; + Fragment(int n, const uint8_t *b, int l, bool last) : + fragmentNum(n), len(l), isLast(last) { memcpy(buf, b, len); }; + }; - struct IncompleteMessage - { - std::shared_ptr msg; - int nextFragmentNum; - uint32_t lastFragmentInsertTime; // in seconds - uint64_t receivedFragmentsBits; - std::set, FragmentCmp> savedFragments; + struct FragmentCmp { + bool operator()(const std::shared_ptr &f1, const std::shared_ptr &f2) const { + return f1->fragmentNum < f2->fragmentNum; + }; + }; - IncompleteMessage (std::shared_ptr&& m): msg (m), nextFragmentNum (0), - lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; - void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); - }; + struct IncompleteMessage { + std::shared_ptr msg; + int nextFragmentNum; + uint32_t lastFragmentInsertTime; // in seconds + uint64_t receivedFragmentsBits; + std::set, FragmentCmp> savedFragments; - struct SentMessage - { - std::vector > fragments; - uint32_t nextResendTime; // in seconds - int numResends; - }; + IncompleteMessage(std::shared_ptr &&m) : msg(m), nextFragmentNum(0), + lastFragmentInsertTime(0), + receivedFragmentsBits(0) {}; - class SSUSession; - class SSUData - { - public: + void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize); + }; - SSUData (SSUSession& session); - ~SSUData (); + struct SentMessage { + std::vector > fragments; + uint32_t nextResendTime; // in seconds + int numResends; + }; - void Start (); - void Stop (); - void CleanUp (uint64_t ts); + class SSUSession; - void ProcessMessage (uint8_t * buf, size_t len); - void FlushReceivedMessage (); - void Send (std::shared_ptr msg); + class SSUData { + public: - void AdjustPacketSize (std::shared_ptr remoteRouter); - void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent); + SSUData(SSUSession &session); - private: + ~SSUData(); - 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 Start(); - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); + void Stop(); - private: + void CleanUp(uint64_t ts); - SSUSession& m_Session; - std::map > m_IncompleteMessages; - std::map > m_SentMessages; - std::unordered_map 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 - }; -} + void ProcessMessage(uint8_t *buf, size_t len); + + void FlushReceivedMessage(); + + void Send(std::shared_ptr msg); + + void AdjustPacketSize(std::shared_ptr remoteRouter); + + 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 > m_IncompleteMessages; + std::map > m_SentMessages; + std::unordered_map 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 diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 76b6486b..a80998da 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -16,1303 +16,1150 @@ #include "SSU.h" #include "SSUSession.h" -namespace i2p -{ -namespace transport -{ - SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr router, bool peerTest ): - TransportSession (router, SSU_TERMINATION_TIMEOUT), - m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_ConnectTimer (GetService ()), - m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), - m_RelayTag (0), m_SentRelayTag (0), m_Data (*this), m_IsDataReceived (false) - { - if (router) - { - // we are client - auto address = IsV6 () ? router->GetSSUV6Address () : router->GetSSUAddress (true); - if (address) m_IntroKey = address->i; - m_Data.AdjustPacketSize (router); // mtu - } - else - { - // we are server - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (address) m_IntroKey = address->i; - } - } +namespace i2p { + namespace transport { + SSUSession::SSUSession(SSUServer &server, boost::asio::ip::udp::endpoint &remoteEndpoint, + std::shared_ptr router, bool peerTest) : + TransportSession(router, SSU_TERMINATION_TIMEOUT), + m_Server(server), m_RemoteEndpoint(remoteEndpoint), m_ConnectTimer(GetService()), + m_IsPeerTest(peerTest), m_State(eSessionStateUnknown), m_IsSessionKey(false), + m_RelayTag(0), m_SentRelayTag(0), m_Data(*this), m_IsDataReceived(false) { + if (router) { + // we are client + auto address = IsV6() ? router->GetSSUV6Address() : router->GetSSUAddress(true); + if (address) m_IntroKey = address->i; + m_Data.AdjustPacketSize(router); // mtu + } else { + // we are server + auto address = IsV6() ? i2p::context.GetRouterInfo().GetSSUV6Address() : + i2p::context.GetRouterInfo().GetSSUAddress(true); + if (address) m_IntroKey = address->i; + } + } - SSUSession::~SSUSession () - { - } + SSUSession::~SSUSession() { + } - boost::asio::io_service& SSUSession::GetService () - { - return m_Server.GetService (); - } + boost::asio::io_service &SSUSession::GetService() { + return m_Server.GetService(); + } - void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) - { - uint8_t sharedKey[256]; - m_DHKeysPair->Agree (pubKey, sharedKey); + void SSUSession::CreateAESandMacKey(const uint8_t *pubKey) { + uint8_t sharedKey[256]; + m_DHKeysPair->Agree(pubKey, sharedKey); - uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey; - if (sharedKey[0] & 0x80) - { - sessionKey[0] = 0; - memcpy (sessionKey + 1, sharedKey, 31); - memcpy (macKey, sharedKey + 31, 32); - } - else if (sharedKey[0]) - { - memcpy (sessionKey, sharedKey, 32); - memcpy (macKey, sharedKey + 32, 32); - } - else - { - // find first non-zero byte - uint8_t * nonZero = sharedKey + 1; - while (!*nonZero) - { - nonZero++; - if (nonZero - sharedKey > 32) - { - LogPrint (eLogWarning, "SSU: First 32 bytes of shared key is all zeros. Ignored"); - return; - } - } + uint8_t *sessionKey = m_SessionKey, *macKey = m_MacKey; + if (sharedKey[0] & 0x80) { + sessionKey[0] = 0; + memcpy(sessionKey + 1, sharedKey, 31); + memcpy(macKey, sharedKey + 31, 32); + } else if (sharedKey[0]) { + memcpy(sessionKey, sharedKey, 32); + memcpy(macKey, sharedKey + 32, 32); + } else { + // find first non-zero byte + uint8_t *nonZero = sharedKey + 1; + while (!*nonZero) { + nonZero++; + if (nonZero - sharedKey > 32) { + LogPrint(eLogWarning, "SSU: First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } - memcpy (sessionKey, nonZero, 32); - SHA256(nonZero, 64 - (nonZero - sharedKey), macKey); - } - m_IsSessionKey = true; - m_SessionKeyEncryption.SetKey (m_SessionKey); - m_SessionKeyDecryption.SetKey (m_SessionKey); - } + memcpy(sessionKey, nonZero, 32); + SHA256(nonZero, 64 - (nonZero - sharedKey), macKey); + } + m_IsSessionKey = true; + m_SessionKeyEncryption.SetKey(m_SessionKey); + m_SessionKeyDecryption.SetKey(m_SessionKey); + } - void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - m_NumReceivedBytes += len; - i2p::transport::transports.UpdateReceivedBytes (len); - if (m_State == eSessionStateIntroduced) - { - // HolePunch received - LogPrint (eLogDebug, "SSU: HolePunch of ", len, " bytes received"); - m_State = eSessionStateUnknown; - Connect (); - } - else - { - if (!len) return; // ignore zero-length packets - if (m_State == eSessionStateEstablished) - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + void + SSUSession::ProcessNextMessage(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint) { + m_NumReceivedBytes += len; + i2p::transport::transports.UpdateReceivedBytes(len); + if (m_State == eSessionStateIntroduced) { + // HolePunch received + LogPrint(eLogDebug, "SSU: HolePunch of ", len, " bytes received"); + m_State = eSessionStateUnknown; + Connect(); + } else { + if (!len) return; // ignore zero-length packets + if (m_State == eSessionStateEstablished) + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); - if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first - DecryptSessionKey (buf, len); - else - { - if (m_State == eSessionStateEstablished) Reset (); // new session key required - // try intro key depending on side - if (Validate (buf, len, m_IntroKey)) - Decrypt (buf, len, m_IntroKey); - else - { - // try own intro key - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported"); - return; - } - if (Validate (buf, len, address->i)) - Decrypt (buf, len, address->i); - else - { - LogPrint (eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint); - m_Server.DeleteSession (shared_from_this ()); - return; - } - } - } - // successfully decrypted - ProcessMessage (buf, len, senderEndpoint); - } - } + if (m_IsSessionKey && Validate(buf, len, m_MacKey)) // try session key first + DecryptSessionKey(buf, len); + else { + if (m_State == eSessionStateEstablished) Reset(); // new session key required + // try intro key depending on side + if (Validate(buf, len, m_IntroKey)) + Decrypt(buf, len, m_IntroKey); + else { + // try own intro key + auto address = IsV6() ? i2p::context.GetRouterInfo().GetSSUV6Address() : + i2p::context.GetRouterInfo().GetSSUAddress(true); + if (!address) { + LogPrint(eLogInfo, "SSU: SSU is not supported"); + return; + } + if (Validate(buf, len, address->i)) + Decrypt(buf, len, address->i); + else { + LogPrint(eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint); + m_Server.DeleteSession(shared_from_this()); + return; + } + } + } + // successfully decrypted + ProcessMessage(buf, len, senderEndpoint); + } + } - size_t SSUSession::GetSSUHeaderSize (const uint8_t * buf) const - { - size_t s = sizeof (SSUHeader); - if (((const SSUHeader *)buf)->IsExtendedOptions ()) - s += buf[s] + 1; // byte right after header is extended options length - return s; - } + size_t SSUSession::GetSSUHeaderSize(const uint8_t *buf) const { + size_t s = sizeof(SSUHeader); + if (((const SSUHeader *) buf)->IsExtendedOptions()) + s += buf[s] + 1; // byte right after header is extended options length + return s; + } - void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - len -= (len & 0x0F); // %16, delete extra padding - if (len <= sizeof (SSUHeader)) return; // drop empty message - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - auto headerSize = GetSSUHeaderSize (buf); - if (headerSize >= len) - { - LogPrint (eLogError, "SSU: SSU header size ", headerSize, " exceeds packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - switch (header->GetPayloadType ()) - { - case PAYLOAD_TYPE_DATA: - ProcessData (buf + headerSize, len - headerSize); - break; - case PAYLOAD_TYPE_SESSION_REQUEST: - ProcessSessionRequest (buf, len); // buf with header - break; - case PAYLOAD_TYPE_SESSION_CREATED: - ProcessSessionCreated (buf, len); // buf with header - break; - case PAYLOAD_TYPE_SESSION_CONFIRMED: - ProcessSessionConfirmed (buf, len); // buf with header - break; - case PAYLOAD_TYPE_PEER_TEST: - LogPrint (eLogDebug, "SSU: Peer test received"); - ProcessPeerTest (buf + headerSize, len - headerSize, senderEndpoint); - break; - case PAYLOAD_TYPE_SESSION_DESTROYED: - { - LogPrint (eLogDebug, "SSU: Session destroy received"); - m_Server.DeleteSession (shared_from_this ()); - break; - } - case PAYLOAD_TYPE_RELAY_RESPONSE: - ProcessRelayResponse (buf + headerSize, len - headerSize); - if (m_State != eSessionStateEstablished) - m_Server.DeleteSession (shared_from_this ()); - break; - case PAYLOAD_TYPE_RELAY_REQUEST: - LogPrint (eLogDebug, "SSU: Relay request received"); - ProcessRelayRequest (buf + headerSize, len - headerSize, senderEndpoint); - break; - case PAYLOAD_TYPE_RELAY_INTRO: - LogPrint (eLogDebug, "SSU: Relay intro received"); - ProcessRelayIntro (buf + headerSize, len - headerSize); - break; - default: - LogPrint (eLogWarning, "SSU: Unexpected payload type ", (int)header->GetPayloadType ()); - } - } + void + SSUSession::ProcessMessage(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint) { + len -= (len & 0x0F); // %16, delete extra padding + if (len <= sizeof(SSUHeader)) return; // drop empty message + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + auto headerSize = GetSSUHeaderSize(buf); + if (headerSize >= len) { + LogPrint(eLogError, "SSU: SSU header size ", headerSize, " exceeds packet length ", len); + return; + } + SSUHeader *header = (SSUHeader *) buf; + switch (header->GetPayloadType()) { + case PAYLOAD_TYPE_DATA: + ProcessData(buf + headerSize, len - headerSize); + break; + case PAYLOAD_TYPE_SESSION_REQUEST: + ProcessSessionRequest(buf, len); // buf with header + break; + case PAYLOAD_TYPE_SESSION_CREATED: + ProcessSessionCreated(buf, len); // buf with header + break; + case PAYLOAD_TYPE_SESSION_CONFIRMED: + ProcessSessionConfirmed(buf, len); // buf with header + break; + case PAYLOAD_TYPE_PEER_TEST: + LogPrint(eLogDebug, "SSU: Peer test received"); + ProcessPeerTest(buf + headerSize, len - headerSize, senderEndpoint); + break; + case PAYLOAD_TYPE_SESSION_DESTROYED: { + LogPrint(eLogDebug, "SSU: Session destroy received"); + m_Server.DeleteSession(shared_from_this()); + break; + } + case PAYLOAD_TYPE_RELAY_RESPONSE: + ProcessRelayResponse(buf + headerSize, len - headerSize); + if (m_State != eSessionStateEstablished) + m_Server.DeleteSession(shared_from_this()); + break; + case PAYLOAD_TYPE_RELAY_REQUEST: + LogPrint(eLogDebug, "SSU: Relay request received"); + ProcessRelayRequest(buf + headerSize, len - headerSize, senderEndpoint); + break; + case PAYLOAD_TYPE_RELAY_INTRO: + LogPrint(eLogDebug, "SSU: Relay intro received"); + ProcessRelayIntro(buf + headerSize, len - headerSize); + break; + default: + LogPrint(eLogWarning, "SSU: Unexpected payload type ", (int) header->GetPayloadType()); + } + } - void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SSU message: Session request"); - bool sendRelayTag = true; - auto headerSize = sizeof (SSUHeader); - if (((SSUHeader *)buf)->IsExtendedOptions ()) - { - uint8_t extendedOptionsLen = buf[headerSize]; - headerSize++; - if (extendedOptionsLen >= 2) // options are presented - { - uint16_t flags = bufbe16toh (buf + headerSize); - sendRelayTag = flags & EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG; - } - headerSize += extendedOptionsLen; - } - if (headerSize >= len) - { - LogPrint (eLogError, "SSU message: Session request header size ", headerSize, " exceeds packet length ", len); - return; - } - if (!m_DHKeysPair) - { - auto pair = std::make_shared (); - pair->GenerateKeys (); - m_DHKeysPair = pair; - } - CreateAESandMacKey (buf + headerSize); - SendSessionCreated (buf + headerSize, sendRelayTag); - } + void SSUSession::ProcessSessionRequest(const uint8_t *buf, size_t len) { + LogPrint(eLogDebug, "SSU message: Session request"); + bool sendRelayTag = true; + auto headerSize = sizeof(SSUHeader); + if (((SSUHeader *) buf)->IsExtendedOptions()) { + uint8_t extendedOptionsLen = buf[headerSize]; + headerSize++; + if (extendedOptionsLen >= 2) // options are presented + { + uint16_t flags = bufbe16toh(buf + headerSize); + sendRelayTag = flags & EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG; + } + headerSize += extendedOptionsLen; + } + if (headerSize >= len) { + LogPrint(eLogError, "SSU message: Session request header size ", headerSize, " exceeds packet length ", + len); + return; + } + if (!m_DHKeysPair) { + auto pair = std::make_shared(); + pair->GenerateKeys(); + m_DHKeysPair = pair; + } + CreateAESandMacKey(buf + headerSize); + SendSessionCreated(buf + headerSize, sendRelayTag); + } - void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) - { - if (!IsOutgoing () || !m_DHKeysPair) - { - LogPrint (eLogWarning, "SSU: Unsolicited session created message"); - return; - } + void SSUSession::ProcessSessionCreated(uint8_t *buf, size_t len) { + if (!IsOutgoing() || !m_DHKeysPair) { + LogPrint(eLogWarning, "SSU: Unsolicited session created message"); + return; + } - LogPrint (eLogDebug, "SSU message: session created"); - m_ConnectTimer.cancel (); // connect timer - SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time - auto headerSize = GetSSUHeaderSize (buf); - if (headerSize >= len) - { - LogPrint (eLogError, "SSU message: Session created header size ", headerSize, " exceeds packet length ", len); - return; - } - uint8_t * payload = buf + headerSize; - uint8_t * y = payload; - CreateAESandMacKey (y); - s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x - s.Insert (y, 256); // y - payload += 256; - boost::asio::ip::address ourIP; - uint16_t ourPort = 0; - auto addressAndPortLen = ExtractIPAddressAndPort (payload, len, ourIP, ourPort); - if (!addressAndPortLen) return; - uint8_t * ourAddressAndPort = payload + 1; - payload += addressAndPortLen; - addressAndPortLen--; // -1 byte address size - s.Insert (ourAddressAndPort, addressAndPortLen); // address + port - if (m_RemoteEndpoint.address ().is_v4 ()) - s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4 - else - s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6 - s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port - s.Insert (payload, 8); // relayTag and signed on time - m_RelayTag = bufbe32toh (payload); - payload += 4; // relayTag - uint32_t signedOnTime = bufbe32toh(payload); - payload += 4; // signed on time - // decrypt signature - size_t signatureLen = m_RemoteIdentity->GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv); - m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); // TODO: non-const payload - // verify signature - if (s.Verify (m_RemoteIdentity, payload)) - { - if (ourIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - int offset = (int)ts - signedOnTime; - if (m_Server.IsSyncClockFromPeers ()) - { - if (std::abs (offset) > SSU_CLOCK_THRESHOLD) - { - LogPrint (eLogWarning, "SSU: Clock adjusted by ", -offset, " seconds"); - i2p::util::AdjustTimeOffset (-offset); - } - } - else if (std::abs (offset) > SSU_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU: Clock skew detected ", offset, ". Check your clock"); - i2p::context.SetError (eRouterErrorClockSkew); - } - } - LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); - if (!i2p::util::net::IsInReservedRange (ourIP)) - { - i2p::context.UpdateAddress (ourIP); - SendSessionConfirmed (y, ourAddressAndPort, addressAndPortLen); - } - else - { - LogPrint (eLogError, "SSU: External address ", ourIP.to_string (), " is in reserved range"); - Failed (); - } - } - else - { - LogPrint (eLogError, "SSU: Message 'created' signature verification failed"); - Failed (); - } - } + LogPrint(eLogDebug, "SSU message: session created"); + m_ConnectTimer.cancel(); // connect timer + SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time + auto headerSize = GetSSUHeaderSize(buf); + if (headerSize >= len) { + LogPrint(eLogError, "SSU message: Session created header size ", headerSize, " exceeds packet length ", + len); + return; + } + uint8_t *payload = buf + headerSize; + uint8_t *y = payload; + CreateAESandMacKey(y); + s.Insert(m_DHKeysPair->GetPublicKey(), 256); // x + s.Insert(y, 256); // y + payload += 256; + boost::asio::ip::address ourIP; + uint16_t ourPort = 0; + auto addressAndPortLen = ExtractIPAddressAndPort(payload, len, ourIP, ourPort); + if (!addressAndPortLen) return; + uint8_t *ourAddressAndPort = payload + 1; + payload += addressAndPortLen; + addressAndPortLen--; // -1 byte address size + s.Insert(ourAddressAndPort, addressAndPortLen); // address + port + if (m_RemoteEndpoint.address().is_v4()) + s.Insert(m_RemoteEndpoint.address().to_v4().to_bytes().data(), 4); // remote IP v4 + else + s.Insert(m_RemoteEndpoint.address().to_v6().to_bytes().data(), 16); // remote IP v6 + s.Insert(htobe16(m_RemoteEndpoint.port())); // remote port + s.Insert(payload, 8); // relayTag and signed on time + m_RelayTag = bufbe32toh(payload); + payload += 4; // relayTag + uint32_t signedOnTime = bufbe32toh(payload); + payload += 4; // signed on time + // decrypt signature + size_t signatureLen = m_RemoteIdentity->GetSignatureLen(); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + m_SessionKeyDecryption.SetIV(((SSUHeader *) buf)->iv); + m_SessionKeyDecryption.Decrypt(payload, signatureLen, payload); // TODO: non-const payload + // verify signature + if (s.Verify(m_RemoteIdentity, payload)) { + if (ourIP.is_v4() && i2p::context.GetStatus() == eRouterStatusTesting) { + auto ts = i2p::util::GetSecondsSinceEpoch(); + int offset = (int) ts - signedOnTime; + if (m_Server.IsSyncClockFromPeers()) { + if (std::abs(offset) > SSU_CLOCK_THRESHOLD) { + LogPrint(eLogWarning, "SSU: Clock adjusted by ", -offset, " seconds"); + i2p::util::AdjustTimeOffset(-offset); + } + } else if (std::abs(offset) > SSU_CLOCK_SKEW) { + LogPrint(eLogError, "SSU: Clock skew detected ", offset, ". Check your clock"); + i2p::context.SetError(eRouterErrorClockSkew); + } + } + LogPrint(eLogInfo, "SSU: Our external address is ", ourIP.to_string(), ":", ourPort); + if (!i2p::util::net::IsInReservedRange(ourIP)) { + i2p::context.UpdateAddress(ourIP); + SendSessionConfirmed(y, ourAddressAndPort, addressAndPortLen); + } else { + LogPrint(eLogError, "SSU: External address ", ourIP.to_string(), " is in reserved range"); + Failed(); + } + } else { + LogPrint(eLogError, "SSU: Message 'created' signature verification failed"); + Failed(); + } + } - void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SSU: Session confirmed received"); - m_ConnectTimer.cancel (); - auto headerSize = GetSSUHeaderSize (buf); - if (headerSize >= len) - { - LogPrint (eLogError, "SSU: Session confirmed header size ", headerSize, " exceeds packet length ", len); - return; - } - const uint8_t * payload = buf + headerSize; - payload++; // identity fragment info - uint16_t identitySize = bufbe16toh (payload); - if (identitySize + headerSize + 7 > len) // 7 = fragment info + fragment size + signed on time - { - LogPrint (eLogError, "SSU: Session confirmed identity size ", identitySize, " exceeds packet length ", len); - return; - } - payload += 2; // size of identity fragment - auto identity = std::make_shared (payload, identitySize); - auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already - SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity); - m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ()); - payload += identitySize; // identity - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t signedOnTime = bufbe32toh(payload); - if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU: Message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew"); - Failed (); - return; - } - if (m_SignedData) - m_SignedData->Insert (payload, 4); // insert Alice's signed on time - payload += 4; // signed-on time - size_t fullSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen (); - size_t paddingSize = fullSize & 0x0F; // %16 - if (paddingSize > 0) paddingSize = 16 - paddingSize; - payload += paddingSize; - if (fullSize + paddingSize > len) - { - LogPrint (eLogError, "SSU: Session confirmed message is too short ", len); - return; - } - // verify signature - if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload)) - { - m_Data.Send (CreateDeliveryStatusMsg (0)); - Established (); - } - else - { - LogPrint (eLogError, "SSU: Message 'confirmed' signature verification failed"); - Failed (); - } - } + void SSUSession::ProcessSessionConfirmed(const uint8_t *buf, size_t len) { + LogPrint(eLogDebug, "SSU: Session confirmed received"); + m_ConnectTimer.cancel(); + auto headerSize = GetSSUHeaderSize(buf); + if (headerSize >= len) { + LogPrint(eLogError, "SSU: Session confirmed header size ", headerSize, " exceeds packet length ", len); + return; + } + const uint8_t *payload = buf + headerSize; + payload++; // identity fragment info + uint16_t identitySize = bufbe16toh(payload); + if (identitySize + headerSize + 7 > len) // 7 = fragment info + fragment size + signed on time + { + LogPrint(eLogError, "SSU: Session confirmed identity size ", identitySize, " exceeds packet length ", + len); + return; + } + payload += 2; // size of identity fragment + auto identity = std::make_shared(payload, identitySize); + auto existing = i2p::data::netdb.FindRouter(identity->GetIdentHash()); // check if exists already + SetRemoteIdentity(existing ? existing->GetRouterIdentity() : identity); + m_Data.UpdatePacketSize(m_RemoteIdentity->GetIdentHash()); + payload += identitySize; // identity + auto ts = i2p::util::GetSecondsSinceEpoch(); + uint32_t signedOnTime = bufbe32toh(payload); + if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) { + LogPrint(eLogError, "SSU: Message 'confirmed' time difference ", (int) ts - signedOnTime, + " exceeds clock skew"); + Failed(); + return; + } + if (m_SignedData) + m_SignedData->Insert(payload, 4); // insert Alice's signed on time + payload += 4; // signed-on time + size_t fullSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen(); + size_t paddingSize = fullSize & 0x0F; // %16 + if (paddingSize > 0) paddingSize = 16 - paddingSize; + payload += paddingSize; + if (fullSize + paddingSize > len) { + LogPrint(eLogError, "SSU: Session confirmed message is too short ", len); + return; + } + // verify signature + if (m_SignedData && m_SignedData->Verify(m_RemoteIdentity, payload)) { + m_Data.Send(CreateDeliveryStatusMsg(0)); + Established(); + } else { + LogPrint(eLogError, "SSU: Message 'confirmed' signature verification failed"); + Failed(); + } + } - void SSUSession::SendSessionRequest () - { - uint8_t buf[320 + 18] = {0}; // 304 bytes for ipv4, 320 for ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - uint8_t flag = 0; - // fill extended options, 3 bytes extended options don't change message size - bool isV4 = m_RemoteEndpoint.address ().is_v4 (); - if ((isV4 && i2p::context.GetStatus () == eRouterStatusOK) || - (!isV4 && i2p::context.GetStatusV6 () == eRouterStatusOK)) // we don't need relays - { - // tell out peer to now assign relay tag - flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; - *payload = 2; payload++; // 1 byte length - uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG - htobe16buf (payload, flags); - payload += 2; - } - // fill payload - memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); // x - if (isV4) - { - payload[256] = 4; - memcpy (payload + 257, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); - } - else - { - payload[256] = 16; - memcpy (payload + 257, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); - } - // encrypt and send - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, m_IntroKey, iv, m_IntroKey, flag); - m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); - } + void SSUSession::SendSessionRequest() { + uint8_t buf[320 + 18] = {0}; // 304 bytes for ipv4, 320 for ipv6 + uint8_t *payload = buf + sizeof(SSUHeader); + uint8_t flag = 0; + // fill extended options, 3 bytes extended options don't change message size + bool isV4 = m_RemoteEndpoint.address().is_v4(); + if ((isV4 && i2p::context.GetStatus() == eRouterStatusOK) || + (!isV4 && i2p::context.GetStatusV6() == eRouterStatusOK)) // we don't need relays + { + // tell out peer to now assign relay tag + flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; + *payload = 2; + payload++; // 1 byte length + uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG + htobe16buf(payload, flags); + payload += 2; + } + // fill payload + memcpy(payload, m_DHKeysPair->GetPublicKey(), 256); // x + if (isV4) { + payload[256] = 4; + memcpy(payload + 257, m_RemoteEndpoint.address().to_v4().to_bytes().data(), 4); + } else { + payload[256] = 16; + memcpy(payload + 257, m_RemoteEndpoint.address().to_v6().to_bytes().data(), 16); + } + // encrypt and send + uint8_t iv[16]; + RAND_bytes(iv, 16); // random iv + FillHeaderAndEncrypt(PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, m_IntroKey, iv, m_IntroKey, flag); + m_Server.Send(buf, isV4 ? 304 : 320, m_RemoteEndpoint); + } - void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce) - { - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported"); - return; - } + void SSUSession::SendRelayRequest(const i2p::data::RouterInfo::Introducer &introducer, uint32_t nonce) { + auto address = IsV6() ? i2p::context.GetRouterInfo().GetSSUV6Address() : + i2p::context.GetRouterInfo().GetSSUAddress(true); + if (!address) { + LogPrint(eLogInfo, "SSU: SSU is not supported"); + return; + } - uint8_t buf[96 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - htobe32buf (payload, introducer.iTag); - payload += 4; - *payload = 0; // no address - payload++; - htobuf16(payload, 0); // port = 0 - payload += 2; - *payload = 0; // challenge - payload++; - memcpy (payload, (const uint8_t *)address->i, 32); - payload += 32; - htobe32buf (payload, nonce); // nonce + uint8_t buf[96 + 18] = {0}; + uint8_t *payload = buf + sizeof(SSUHeader); + htobe32buf(payload, introducer.iTag); + payload += 4; + *payload = 0; // no address + payload++; + htobuf16(payload, 0); // port = 0 + payload += 2; + *payload = 0; // challenge + payload++; + memcpy(payload, (const uint8_t *) address->i, 32); + payload += 32; + htobe32buf(payload, nonce); // nonce - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - if (m_State == eSessionStateEstablished) - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); - else - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); - m_Server.Send (buf, 96, m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU: Relay request sent"); - } + uint8_t iv[16]; + RAND_bytes(iv, 16); // random iv + if (m_State == eSessionStateEstablished) + FillHeaderAndEncrypt(PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); + else + FillHeaderAndEncrypt(PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); + m_Server.Send(buf, 96, m_RemoteEndpoint); + LogPrint(eLogDebug, "SSU: Relay request sent"); + } - void SSUSession::SendSessionCreated (const uint8_t * x, bool sendRelayTag) - { - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported"); - return; - } - SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time - s.Insert (x, 256); // x + void SSUSession::SendSessionCreated(const uint8_t *x, bool sendRelayTag) { + auto address = IsV6() ? i2p::context.GetRouterInfo().GetSSUV6Address() : + i2p::context.GetRouterInfo().GetSSUAddress(true); //v4 only + if (!address) { + LogPrint(eLogInfo, "SSU: SSU is not supported"); + return; + } + SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time + s.Insert(x, 256); // x - uint8_t buf[384 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); - s.Insert (payload, 256); // y - payload += 256; - if (m_RemoteEndpoint.address ().is_v4 ()) - { - // ipv4 - *payload = 4; - payload++; - memcpy (payload, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); - s.Insert (payload, 4); // remote endpoint IP V4 - payload += 4; - } - else - { - // ipv6 - *payload = 16; - payload++; - memcpy (payload, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); - s.Insert (payload, 16); // remote endpoint IP V6 - payload += 16; - } - htobe16buf (payload, m_RemoteEndpoint.port ()); - s.Insert (payload, 2); // remote port - payload += 2; - if (address->host.is_v4 ()) - s.Insert (address->host.to_v4 ().to_bytes ().data (), 4); // our IP V4 - else - s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6 - s.Insert (htobe16 (address->port)); // our port - if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer (!IsV6 ())) - { - RAND_bytes((uint8_t *)&m_SentRelayTag, 4); - if (!m_SentRelayTag) m_SentRelayTag = 1; - } - htobe32buf (payload, m_SentRelayTag); - payload += 4; // relay tag - htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time - payload += 4; - s.Insert (payload - 8, 4); // relayTag - // we have to store this signed data for session confirmed - // same data but signed on time, it will Alice's there - m_SignedData = std::unique_ptr(new SignedData (s)); - s.Insert (payload - 4, 4); // BOB's signed on time - s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature + uint8_t buf[384 + 18] = {0}; + uint8_t *payload = buf + sizeof(SSUHeader); + memcpy(payload, m_DHKeysPair->GetPublicKey(), 256); + s.Insert(payload, 256); // y + payload += 256; + if (m_RemoteEndpoint.address().is_v4()) { + // ipv4 + *payload = 4; + payload++; + memcpy(payload, m_RemoteEndpoint.address().to_v4().to_bytes().data(), 4); + s.Insert(payload, 4); // remote endpoint IP V4 + payload += 4; + } else { + // ipv6 + *payload = 16; + payload++; + memcpy(payload, m_RemoteEndpoint.address().to_v6().to_bytes().data(), 16); + s.Insert(payload, 16); // remote endpoint IP V6 + payload += 16; + } + htobe16buf(payload, m_RemoteEndpoint.port()); + s.Insert(payload, 2); // remote port + payload += 2; + if (address->host.is_v4()) + s.Insert(address->host.to_v4().to_bytes().data(), 4); // our IP V4 + else + s.Insert(address->host.to_v6().to_bytes().data(), 16); // our IP V6 + s.Insert(htobe16(address->port)); // our port + if (sendRelayTag && i2p::context.GetRouterInfo().IsIntroducer(!IsV6())) { + RAND_bytes((uint8_t * ) & m_SentRelayTag, 4); + if (!m_SentRelayTag) m_SentRelayTag = 1; + } + htobe32buf(payload, m_SentRelayTag); + payload += 4; // relay tag + htobe32buf(payload, i2p::util::GetSecondsSinceEpoch()); // signed on time + payload += 4; + s.Insert(payload - 8, 4); // relayTag + // we have to store this signed data for session confirmed + // same data but signed on time, it will Alice's there + m_SignedData = std::unique_ptr(new SignedData(s)); + s.Insert(payload - 4, 4); // BOB's signed on time + s.Sign(i2p::context.GetPrivateKeys(), payload); // DSA signature - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - // encrypt signature and padding with newly created session key - size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) - { - // fill random padding - RAND_bytes(payload + signatureLen, (16 - paddingSize)); - signatureLen += (16 - paddingSize); - } - m_SessionKeyEncryption.SetIV (iv); - m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload); - payload += signatureLen; - size_t msgLen = payload - buf; + uint8_t iv[16]; + RAND_bytes(iv, 16); // random iv + // encrypt signature and padding with newly created session key + size_t signatureLen = i2p::context.GetIdentity()->GetSignatureLen(); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) { + // fill random padding + RAND_bytes(payload + signatureLen, (16 - paddingSize)); + signatureLen += (16 - paddingSize); + } + m_SessionKeyEncryption.SetIV(iv); + m_SessionKeyEncryption.Encrypt(payload, signatureLen, payload); + payload += signatureLen; + size_t msgLen = payload - buf; - // encrypt message with intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, m_IntroKey, iv, m_IntroKey); - Send (buf, msgLen); - } + // encrypt message with intro key + FillHeaderAndEncrypt(PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, m_IntroKey, iv, m_IntroKey); + Send(buf, msgLen); + } - void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen) - { - uint8_t buf[512 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 1; // 1 fragment - payload++; // info - size_t identLen = i2p::context.GetIdentity ()->GetFullLen (); // 387+ bytes - htobe16buf (payload, identLen); - payload += 2; // cursize - i2p::context.GetIdentity ()->ToBuffer (payload, identLen); - payload += identLen; - uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); - htobe32buf (payload, signedOnTime); // signed on time - payload += 4; - auto signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); - size_t paddingSize = ((payload - buf) + signatureLen)%16; - if (paddingSize > 0) paddingSize = 16 - paddingSize; - RAND_bytes(payload, paddingSize); // fill padding with random - payload += paddingSize; // padding size - // signature - SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time - s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x - s.Insert (y, 256); // y - s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party - if (m_RemoteEndpoint.address ().is_v4 ()) - s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP V4 - else - s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP V6 - s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port - s.Insert (htobe32 (m_RelayTag)); // relay tag - s.Insert (htobe32 (signedOnTime)); // signed on time - s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature - payload += signatureLen; + void SSUSession::SendSessionConfirmed(const uint8_t *y, const uint8_t *ourAddress, size_t ourAddressLen) { + uint8_t buf[512 + 18] = {0}; + uint8_t *payload = buf + sizeof(SSUHeader); + *payload = 1; // 1 fragment + payload++; // info + size_t identLen = i2p::context.GetIdentity()->GetFullLen(); // 387+ bytes + htobe16buf(payload, identLen); + payload += 2; // cursize + i2p::context.GetIdentity()->ToBuffer(payload, identLen); + payload += identLen; + uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch(); + htobe32buf(payload, signedOnTime); // signed on time + payload += 4; + auto signatureLen = i2p::context.GetIdentity()->GetSignatureLen(); + size_t paddingSize = ((payload - buf) + signatureLen) % 16; + if (paddingSize > 0) paddingSize = 16 - paddingSize; + RAND_bytes(payload, paddingSize); // fill padding with random + payload += paddingSize; // padding size + // signature + SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time + s.Insert(m_DHKeysPair->GetPublicKey(), 256); // x + s.Insert(y, 256); // y + s.Insert(ourAddress, ourAddressLen); // our address/port as seem by party + if (m_RemoteEndpoint.address().is_v4()) + s.Insert(m_RemoteEndpoint.address().to_v4().to_bytes().data(), 4); // remote IP V4 + else + s.Insert(m_RemoteEndpoint.address().to_v6().to_bytes().data(), 16); // remote IP V6 + s.Insert(htobe16(m_RemoteEndpoint.port())); // remote port + s.Insert(htobe32(m_RelayTag)); // relay tag + s.Insert(htobe32(signedOnTime)); // signed on time + s.Sign(i2p::context.GetPrivateKeys(), payload); // DSA signature + payload += signatureLen; - size_t msgLen = payload - buf; - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); - Send (buf, msgLen); - } + size_t msgLen = payload - buf; + uint8_t iv[16]; + RAND_bytes(iv, 16); // random iv + // encrypt message with session key + FillHeaderAndEncrypt(PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); + Send(buf, msgLen); + } - void SSUSession::ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) - { - uint32_t relayTag = bufbe32toh (buf); - auto session = m_Server.FindRelaySession (relayTag); - if (session) - { - buf += 4; // relay tag - uint8_t size = *buf; - buf++; // size - buf += size; // address - buf += 2; // port - uint8_t challengeSize = *buf; - buf++; // challenge size - buf += challengeSize; - const uint8_t * introKey = buf; - buf += 32; // introkey - uint32_t nonce = bufbe32toh (buf); - SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); - SendRelayIntro (session, from); - } - } + void + SSUSession::ProcessRelayRequest(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &from) { + uint32_t relayTag = bufbe32toh(buf); + auto session = m_Server.FindRelaySession(relayTag); + if (session) { + buf += 4; // relay tag + uint8_t size = *buf; + buf++; // size + buf += size; // address + buf += 2; // port + uint8_t challengeSize = *buf; + buf++; // challenge size + buf += challengeSize; + const uint8_t *introKey = buf; + buf += 32; // introkey + uint32_t nonce = bufbe32toh(buf); + SendRelayResponse(nonce, from, introKey, session->m_RemoteEndpoint); + SendRelayIntro(session, from); + } + } - void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, - const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) - { - bool isV4 = to.address ().is_v4 (); // Charle's - bool isV4A = from.address ().is_v4 (); // Alice's - if ((isV4 && !isV4A) || (!isV4 && isV4A)) - { - LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay response"); - return; - } - uint8_t buf[80 + 18] = {0}; // 64 for ipv4 and 80 for ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - // Charlie - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, to.address ().to_v4 ().to_bytes ().data (), 4); // Charlie's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, to.address ().to_v6 ().to_bytes ().data (), 16); // Charlie's IP V6 - payload += 16; // address - } - htobe16buf (payload, to.port ()); // Charlie's port - payload += 2; // port - // Alice - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 - payload += 16; // address - } - htobe16buf (payload, from.port ()); // Alice's port - payload += 2; // port - htobe32buf (payload, nonce); + void SSUSession::SendRelayResponse(uint32_t nonce, const boost::asio::ip::udp::endpoint &from, + const uint8_t *introKey, const boost::asio::ip::udp::endpoint &to) { + bool isV4 = to.address().is_v4(); // Charle's + bool isV4A = from.address().is_v4(); // Alice's + if ((isV4 && !isV4A) || (!isV4 && isV4A)) { + LogPrint(eLogWarning, + "SSU: Charlie's IP and Alice's IP belong to different networks for relay response"); + return; + } + uint8_t buf[80 + 18] = {0}; // 64 for ipv4 and 80 for ipv6 + uint8_t *payload = buf + sizeof(SSUHeader); + // Charlie + if (isV4) { + *payload = 4; + payload++; // size + memcpy(payload, to.address().to_v4().to_bytes().data(), 4); // Charlie's IP V4 + payload += 4; // address + } else { + *payload = 16; + payload++; // size + memcpy(payload, to.address().to_v6().to_bytes().data(), 16); // Charlie's IP V6 + payload += 16; // address + } + htobe16buf(payload, to.port()); // Charlie's port + payload += 2; // port + // Alice + if (isV4) { + *payload = 4; + payload++; // size + memcpy(payload, from.address().to_v4().to_bytes().data(), 4); // Alice's IP V4 + payload += 4; // address + } else { + *payload = 16; + payload++; // size + memcpy(payload, from.address().to_v6().to_bytes().data(), 16); // Alice's IP V6 + payload += 16; // address + } + htobe16buf(payload, from.port()); // Alice's port + payload += 2; // port + htobe32buf(payload, nonce); - if (m_State == eSessionStateEstablished) - { - // encrypt with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80); - Send (buf, isV4 ? 64 : 80); - } - else - { - // ecrypt with Alice's intro key - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); - m_Server.Send (buf, isV4 ? 64 : 80, from); - } - LogPrint (eLogDebug, "SSU: Relay response sent"); - } + if (m_State == eSessionStateEstablished) { + // encrypt with session key + FillHeaderAndEncrypt(PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80); + Send(buf, isV4 ? 64 : 80); + } else { + // ecrypt with Alice's intro key + uint8_t iv[16]; + RAND_bytes(iv, 16); // random iv + FillHeaderAndEncrypt(PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); + m_Server.Send(buf, isV4 ? 64 : 80, from); + } + LogPrint(eLogDebug, "SSU: Relay response sent"); + } - void SSUSession::SendRelayIntro (std::shared_ptr session, const boost::asio::ip::udp::endpoint& from) - { - if (!session) return; - bool isV4 = from.address ().is_v4 (); // Alice's - bool isV4C = session->m_RemoteEndpoint.address ().is_v4 (); // Charlie's - if ((isV4 && !isV4C) || (!isV4 && isV4C)) - { - LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay intro"); - return; - } - uint8_t buf[64 + 18] = {0}; // 48 for ipv4 and 64 for ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 - payload += 16; // address - } - htobe16buf (payload, from.port ()); // Alice's port - payload += 2; // port - *payload = 0; // challenge size - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, isV4 ? 48 : 64, session->m_SessionKey, iv, session->m_MacKey); - m_Server.Send (buf, isV4 ? 48 : 64, session->m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU: Relay intro sent"); - } + void + SSUSession::SendRelayIntro(std::shared_ptr session, const boost::asio::ip::udp::endpoint &from) { + if (!session) return; + bool isV4 = from.address().is_v4(); // Alice's + bool isV4C = session->m_RemoteEndpoint.address().is_v4(); // Charlie's + if ((isV4 && !isV4C) || (!isV4 && isV4C)) { + LogPrint(eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay intro"); + return; + } + uint8_t buf[64 + 18] = {0}; // 48 for ipv4 and 64 for ipv6 + uint8_t *payload = buf + sizeof(SSUHeader); + if (isV4) { + *payload = 4; + payload++; // size + memcpy(payload, from.address().to_v4().to_bytes().data(), 4); // Alice's IP V4 + payload += 4; // address + } else { + *payload = 16; + payload++; // size + memcpy(payload, from.address().to_v6().to_bytes().data(), 16); // Alice's IP V6 + payload += 16; // address + } + htobe16buf(payload, from.port()); // Alice's port + payload += 2; // port + *payload = 0; // challenge size + uint8_t iv[16]; + RAND_bytes(iv, 16); // random iv + FillHeaderAndEncrypt(PAYLOAD_TYPE_RELAY_INTRO, buf, isV4 ? 48 : 64, session->m_SessionKey, iv, + session->m_MacKey); + m_Server.Send(buf, isV4 ? 48 : 64, session->m_RemoteEndpoint); + LogPrint(eLogDebug, "SSU: Relay intro sent"); + } - void SSUSession::ProcessRelayResponse (const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SSU message: Relay response received"); - boost::asio::ip::address remoteIP; - uint16_t remotePort = 0; - auto remoteSize = ExtractIPAddressAndPort (buf, len, remoteIP, remotePort); - if (!remoteSize) return; - buf += remoteSize; len -= remoteSize; - boost::asio::ip::address ourIP; - uint16_t ourPort = 0; - auto ourSize = ExtractIPAddressAndPort (buf, len, ourIP, ourPort); - if (!ourSize) return; - buf += ourSize; len -= ourSize; - LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); - if (!i2p::util::net::IsInReservedRange (ourIP)) - i2p::context.UpdateAddress (ourIP); - else - LogPrint (eLogError, "SSU: External address ", ourIP.to_string (), " is in reserved range"); - if (ourIP.is_v4 ()) - { - if (ourPort != m_Server.GetPort ()) - { - if (i2p::context.GetStatus () == eRouterStatusTesting) - i2p::context.SetError (eRouterErrorSymmetricNAT); - } - else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT) - i2p::context.SetStatus (eRouterStatusTesting); - } - uint32_t nonce = bufbe32toh (buf); - buf += 4; // nonce - auto it = m_RelayRequests.find (nonce); - if (it != m_RelayRequests.end ()) - { - // check if we are waiting for introduction - boost::asio::ip::udp::endpoint remoteEndpoint (remoteIP, remotePort); - if (!m_Server.FindSession (remoteEndpoint)) - { - // we didn't have correct endpoint when sent relay request - // now we do - LogPrint (eLogInfo, "SSU: RelayReponse connecting to endpoint ", remoteEndpoint); - if ((remoteIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) - m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch - // we assume that HolePunch has been sent by this time and our SessionRequest will go through - m_Server.CreateDirectSession (it->second.first, remoteEndpoint, false); - } - // delete request - m_RelayRequests.erase (it); - // cancel connect timer - m_ConnectTimer.cancel (); - } - else - LogPrint (eLogError, "SSU: Unsolicited RelayResponse, nonce=", nonce); - } + void SSUSession::ProcessRelayResponse(const uint8_t *buf, size_t len) { + LogPrint(eLogDebug, "SSU message: Relay response received"); + boost::asio::ip::address remoteIP; + uint16_t remotePort = 0; + auto remoteSize = ExtractIPAddressAndPort(buf, len, remoteIP, remotePort); + if (!remoteSize) return; + buf += remoteSize; + len -= remoteSize; + boost::asio::ip::address ourIP; + uint16_t ourPort = 0; + auto ourSize = ExtractIPAddressAndPort(buf, len, ourIP, ourPort); + if (!ourSize) return; + buf += ourSize; + len -= ourSize; + LogPrint(eLogInfo, "SSU: Our external address is ", ourIP.to_string(), ":", ourPort); + if (!i2p::util::net::IsInReservedRange(ourIP)) + i2p::context.UpdateAddress(ourIP); + else + LogPrint(eLogError, "SSU: External address ", ourIP.to_string(), " is in reserved range"); + if (ourIP.is_v4()) { + if (ourPort != m_Server.GetPort()) { + if (i2p::context.GetStatus() == eRouterStatusTesting) + i2p::context.SetError(eRouterErrorSymmetricNAT); + } else if (i2p::context.GetStatus() == eRouterStatusError && + i2p::context.GetError() == eRouterErrorSymmetricNAT) + i2p::context.SetStatus(eRouterStatusTesting); + } + uint32_t nonce = bufbe32toh(buf); + buf += 4; // nonce + auto it = m_RelayRequests.find(nonce); + if (it != m_RelayRequests.end()) { + // check if we are waiting for introduction + boost::asio::ip::udp::endpoint remoteEndpoint(remoteIP, remotePort); + if (!m_Server.FindSession(remoteEndpoint)) { + // we didn't have correct endpoint when sent relay request + // now we do + LogPrint(eLogInfo, "SSU: RelayReponse connecting to endpoint ", remoteEndpoint); + if ((remoteIP.is_v4() && i2p::context.GetStatus() == eRouterStatusFirewalled) || + (remoteIP.is_v6() && i2p::context.GetStatusV6() == eRouterStatusFirewalled)) + m_Server.Send(buf, 0, remoteEndpoint); // send HolePunch + // we assume that HolePunch has been sent by this time and our SessionRequest will go through + m_Server.CreateDirectSession(it->second.first, remoteEndpoint, false); + } + // delete request + m_RelayRequests.erase(it); + // cancel connect timer + m_ConnectTimer.cancel(); + } else + LogPrint(eLogError, "SSU: Unsolicited RelayResponse, nonce=", nonce); + } - void SSUSession::ProcessRelayIntro (const uint8_t * buf, size_t len) - { - boost::asio::ip::address ip; - uint16_t port = 0; - ExtractIPAddressAndPort (buf, len, ip, port); - if (!ip.is_unspecified () && port) - // send hole punch of 0 bytes - m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (ip, port)); - } + void SSUSession::ProcessRelayIntro(const uint8_t *buf, size_t len) { + boost::asio::ip::address ip; + uint16_t port = 0; + ExtractIPAddressAndPort(buf, len, ip, port); + if (!ip.is_unspecified() && port) + // send hole punch of 0 bytes + m_Server.Send(buf, 0, boost::asio::ip::udp::endpoint(ip, port)); + } - void SSUSession::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) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - memcpy (header->iv, iv, 16); - header->flag = flag | (payloadType << 4); // MSB is 0 - htobe32buf (header->time, i2p::util::GetSecondsSinceEpoch ()); - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - i2p::crypto::CBCEncryption encryption; - encryption.SetKey (aesKey); - encryption.SetIV (iv); - encryption.Encrypt (encrypted, encryptedLen, encrypted); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, iv, 16); - uint16_t netid = i2p::context.GetNetID (); - htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); - } + void SSUSession::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) { + if (len < sizeof(SSUHeader)) { + LogPrint(eLogError, "SSU: Unexpected packet length ", len); + return; + } + SSUHeader *header = (SSUHeader *) buf; + memcpy(header->iv, iv, 16); + header->flag = flag | (payloadType << 4); // MSB is 0 + htobe32buf(header->time, i2p::util::GetSecondsSinceEpoch()); + uint8_t *encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + i2p::crypto::CBCEncryption encryption; + encryption.SetKey(aesKey); + encryption.SetIV(iv); + encryption.Encrypt(encrypted, encryptedLen, encrypted); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy(buf + len, iv, 16); + uint16_t netid = i2p::context.GetNetID(); + htobe16buf(buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); + i2p::crypto::HMACMD5Digest(encrypted, encryptedLen + 18, macKey, header->mac); + } - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) - { - FillHeaderAndEncrypt (payloadType, buf, len, buf); - } + void SSUSession::FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *buf, size_t len) { + FillHeaderAndEncrypt(payloadType, buf, len, buf); + } - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)out; - RAND_bytes (header->iv, 16); // random iv - m_SessionKeyEncryption.SetIV (header->iv); - SSUHeader * inHeader = (SSUHeader *)in; - inHeader->flag = payloadType << 4; // MSB is 0 - htobe32buf (inHeader->time, i2p::util::GetSecondsSinceEpoch ()); - uint8_t * encrypted = &header->flag, * clear = &inHeader->flag; - uint16_t encryptedLen = len - (encrypted - out); - m_SessionKeyEncryption.Encrypt (clear, encryptedLen, encrypted); - // assume actual out buffer size is 18 (16 + 2) bytes more - memcpy (out + len, header->iv, 16); - uint16_t netid = i2p::context.GetNetID (); - htobe16buf (out + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); - } + void SSUSession::FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *in, size_t len, uint8_t *out) { + if (len < sizeof(SSUHeader)) { + LogPrint(eLogError, "SSU: Unexpected packet length ", len); + return; + } + SSUHeader *header = (SSUHeader *) out; + RAND_bytes(header->iv, 16); // random iv + m_SessionKeyEncryption.SetIV(header->iv); + SSUHeader *inHeader = (SSUHeader *) in; + inHeader->flag = payloadType << 4; // MSB is 0 + htobe32buf(inHeader->time, i2p::util::GetSecondsSinceEpoch()); + uint8_t *encrypted = &header->flag, *clear = &inHeader->flag; + uint16_t encryptedLen = len - (encrypted - out); + m_SessionKeyEncryption.Encrypt(clear, encryptedLen, encrypted); + // assume actual out buffer size is 18 (16 + 2) bytes more + memcpy(out + len, header->iv, 16); + uint16_t netid = i2p::context.GetNetID(); + htobe16buf(out + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); + i2p::crypto::HMACMD5Digest(encrypted, encryptedLen + 18, m_MacKey, header->mac); + } - void SSUSession::Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (aesKey); - decryption.SetIV (header->iv); - decryption.Decrypt (encrypted, encryptedLen, encrypted); - } + void SSUSession::Decrypt(uint8_t *buf, size_t len, const i2p::crypto::AESKey &aesKey) { + if (len < sizeof(SSUHeader)) { + LogPrint(eLogError, "SSU: Unexpected packet length ", len); + return; + } + SSUHeader *header = (SSUHeader *) buf; + uint8_t *encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + i2p::crypto::CBCDecryption decryption; + decryption.SetKey(aesKey); + decryption.SetIV(header->iv); + decryption.Decrypt(encrypted, encryptedLen, encrypted); + } - void SSUSession::DecryptSessionKey (uint8_t * buf, size_t len) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - if (encryptedLen > 0) - { - m_SessionKeyDecryption.SetIV (header->iv); - m_SessionKeyDecryption.Decrypt (encrypted, encryptedLen, encrypted); - } - } + void SSUSession::DecryptSessionKey(uint8_t *buf, size_t len) { + if (len < sizeof(SSUHeader)) { + LogPrint(eLogError, "SSU: Unexpected packet length ", len); + return; + } + SSUHeader *header = (SSUHeader *) buf; + uint8_t *encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + if (encryptedLen > 0) { + m_SessionKeyDecryption.SetIV(header->iv); + m_SessionKeyDecryption.Decrypt(encrypted, encryptedLen, encrypted); + } + } - bool SSUSession::Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return false; - } - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, header->iv, 16); - uint16_t netid = i2p::context.GetNetID (); - htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); - uint8_t digest[16]; - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); - return !memcmp (header->mac, digest, 16); - } + bool SSUSession::Validate(uint8_t *buf, size_t len, const i2p::crypto::MACKey &macKey) { + if (len < sizeof(SSUHeader)) { + LogPrint(eLogError, "SSU: Unexpected packet length ", len); + return false; + } + SSUHeader *header = (SSUHeader *) buf; + uint8_t *encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy(buf + len, header->iv, 16); + uint16_t netid = i2p::context.GetNetID(); + htobe16buf(buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); + uint8_t digest[16]; + i2p::crypto::HMACMD5Digest(encrypted, encryptedLen + 18, macKey, digest); + return !memcmp(header->mac, digest, 16); + } - void SSUSession::Connect () - { - if (m_State == eSessionStateUnknown) - { - ScheduleConnectTimer (); // set connect timer - m_DHKeysPair = std::make_shared (); - m_DHKeysPair->GenerateKeys (); - SendSessionRequest (); - } - } + void SSUSession::Connect() { + if (m_State == eSessionStateUnknown) { + ScheduleConnectTimer(); // set connect timer + m_DHKeysPair = std::make_shared(); + m_DHKeysPair->GenerateKeys(); + SendSessionRequest(); + } + } - void SSUSession::WaitForConnect () - { - if (!IsOutgoing ()) // incoming session - ScheduleConnectTimer (); - else - LogPrint (eLogError, "SSU: Wait for connect for outgoing session"); - } + void SSUSession::WaitForConnect() { + if (!IsOutgoing()) // incoming session + ScheduleConnectTimer(); + else + LogPrint(eLogError, "SSU: Wait for connect for outgoing session"); + } - void SSUSession::ScheduleConnectTimer () - { - m_ConnectTimer.cancel (); - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); -} - - void SSUSession::HandleConnectTimer (const boost::system::error_code& ecode) - { - if (!ecode) - { - // timeout expired - LogPrint (eLogWarning, "SSU: Session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds"); - Failed (); - } - } - - void SSUSession::Introduce (const i2p::data::RouterInfo::Introducer& introducer, - std::shared_ptr to) - { - if (m_State == eSessionStateUnknown) - { - // set connect timer - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - auto ts = i2p::util::GetSecondsSinceEpoch (); - m_RelayRequests.emplace (nonce, std::make_pair (to, ts)); - SendRelayRequest (introducer, nonce); - } - - void SSUSession::WaitForIntroduction () - { - m_State = eSessionStateIntroduced; - // set connect timer - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } - - void SSUSession::Close () - { - SendSessionDestroyed (); - Reset (); - m_State = eSessionStateClosed; - } - - void SSUSession::Reset () - { - m_State = eSessionStateUnknown; - transports.PeerDisconnected (shared_from_this ()); - m_Data.Stop (); - m_ConnectTimer.cancel (); - if (m_SentRelayTag) - { - m_Server.RemoveRelay (m_SentRelayTag); // relay tag is not valid anymore - m_SentRelayTag = 0; - } - m_DHKeysPair = nullptr; - m_SignedData = nullptr; - m_IsSessionKey = false; - } - - void SSUSession::Done () - { - GetService ().post (std::bind (&SSUSession::Failed, shared_from_this ())); - } - - void SSUSession::Established () - { - m_State = eSessionStateEstablished; - m_DHKeysPair = nullptr; - m_SignedData = nullptr; - m_Data.Start (); - transports.PeerConnected (shared_from_this ()); - if (m_IsPeerTest) - SendPeerTest (); - if (m_SentRelayTag) - m_Server.AddRelay (m_SentRelayTag, shared_from_this ()); - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - } - - void SSUSession::Failed () - { - if (m_State != eSessionStateFailed) - { - m_State = eSessionStateFailed; - m_Server.DeleteSession (shared_from_this ()); - } - } - - void SSUSession::SendI2NPMessages (const std::vector >& msgs) - { - GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs)); - } - - void SSUSession::PostI2NPMessages (std::vector > msgs) - { - if (m_State == eSessionStateEstablished) - { - for (const auto& it: msgs) - if (it) - { - if (it->GetLength () <= SSU_MAX_I2NP_MESSAGE_SIZE) - m_Data.Send (it); - else - LogPrint (eLogError, "SSU: I2NP message of size ", it->GetLength (), " can't be sent. Dropped"); - } - } - } - - void SSUSession::ProcessData (uint8_t * buf, size_t len) - { - m_Data.ProcessMessage (buf, len); - m_IsDataReceived = true; - } - - void SSUSession::FlushData () - { - if (m_IsDataReceived) - { - m_Data.FlushReceivedMessage (); - m_IsDataReceived = false; - } - } - - void SSUSession::CleanUp (uint64_t ts) - { - m_Data.CleanUp (ts); - for (auto it = m_RelayRequests.begin (); it != m_RelayRequests.end ();) - { - if (ts > it->second.second + SSU_CONNECT_TIMEOUT) - it = m_RelayRequests.erase (it); - else - ++it; - } - } - - void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - uint32_t nonce = bufbe32toh (buf); // 4 bytes - boost::asio::ip::address addr; // Alice's address - uint16_t port = 0; // and port - auto size = ExtractIPAddressAndPort (buf + 4, len - 4, addr, port); - if (port && (size != 7) && (size != 19)) - { - LogPrint (eLogWarning, "SSU: Address of ", size - 3, " bytes not supported"); - return; - } - const uint8_t * introKey = buf + 4 + size; - switch (m_Server.GetPeerTestParticipant (nonce)) - { - // existing test - case ePeerTestParticipantAlice1: - { - if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob - { - LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Alice"); - if (IsV6 ()) - { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - { - i2p::context.SetStatusV6 (eRouterStatusFirewalled); - m_Server.RescheduleIntroducersUpdateTimerV6 (); - } - } - else if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK - { - i2p::context.SetStatus (eRouterStatusFirewalled); - m_Server.RescheduleIntroducersUpdateTimer (); - } - } - else - { - LogPrint (eLogDebug, "SSU: First peer test from Charlie. We are Alice"); - if (m_State == eSessionStateEstablished) - LogPrint (eLogWarning, "SSU: First peer test from Charlie through established session. We are Alice"); - if (IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); - else - i2p::context.SetStatus (eRouterStatusOK); - m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2); - SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie - } - break; - } - case ePeerTestParticipantAlice2: - { - if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob - LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Alice"); - else - { - // peer test successive - LogPrint (eLogDebug, "SSU: Second peer test from Charlie. We are Alice"); - if (IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); - else - i2p::context.SetStatus (eRouterStatusOK); - m_Server.RemovePeerTest (nonce); - } - break; - } - case ePeerTestParticipantBob: - { - LogPrint (eLogDebug, "SSU: Peer test from Charlie. We are Bob"); - auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest - if (session && session->m_State == eSessionStateEstablished) - { - const auto& ep = session->GetRemoteEndpoint (); // Alice's endpoint as known to Bob - session->SendPeerTest (nonce, ep.address (), ep.port (), introKey, false, true); // send back to Alice - } - m_Server.RemovePeerTest (nonce); // nonce has been used - break; - } - case ePeerTestParticipantCharlie: - { - LogPrint (eLogDebug, "SSU: Peer test from Alice. We are Charlie"); - SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address - m_Server.RemovePeerTest (nonce); // nonce has been used - break; - } - // test not found - case ePeerTestParticipantUnknown: - { - if (m_State == eSessionStateEstablished) - { - // new test - if (port) - { - LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Charlie"); - Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob - if (!addr.is_unspecified () && !i2p::util::net::IsInReservedRange(addr)) - { - m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); - SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob - } - } - else - { - LogPrint (eLogDebug, "SSU: Peer test from Alice. We are Bob"); - auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie - if (session) - { - m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ()); - session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address - } - } - } - else - LogPrint (eLogError, "SSU: Unexpected peer test"); - } - } - } - - void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, - const uint8_t * introKey, bool toAddress, bool sendAddress) - // toAddress is true for Alice<->Chalie communications only - // sendAddress is false if message comes from Alice - { - uint8_t buf[80 + 18] = {0}; - uint8_t iv[16]; - uint8_t * payload = buf + sizeof (SSUHeader); - htobe32buf (payload, nonce); - payload += 4; // nonce - // address and port - if (sendAddress) - { - if (address.is_v4 ()) - { - *payload = 4; - memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4 - } - else if (address.is_v6 ()) - { - *payload = 16; - memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6 - } - else - *payload = 0; - payload += (payload[0] + 1); - } - else - { - *payload = 0; - payload++; //size - } - htobe16buf (payload, port); - payload += 2; // port - // intro key - if (toAddress) - { - // send our intro key to address instead of its own - auto addr = address.is_v4 () ? i2p::context.GetRouterInfo ().GetSSUAddress (true) : // ipv4 - i2p::context.GetRouterInfo ().GetSSUV6Address (); - if (addr) - memcpy (payload, addr->i, 32); // intro key - else - LogPrint (eLogInfo, "SSU: SSU is not supported. Can't send peer test"); - } - else - memcpy (payload, introKey, 32); // intro key - - // send - RAND_bytes (iv, 16); // random iv - if (toAddress) - { - // encrypt message with specified intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); - boost::asio::ip::udp::endpoint e (address, port); - m_Server.Send (buf, 80, e); - } - else - { - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); - Send (buf, 80); - } - } - - void SSUSession::SendPeerTest () - { - // we are Alice - LogPrint (eLogDebug, "SSU: Sending peer test"); - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported. Can't send peer test"); - return; - } - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - if (!nonce) nonce = 1; - m_IsPeerTest = false; - m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ()); - SendPeerTest (nonce, boost::asio::ip::address(), 0, address->i, false, false); // address and port always zero for Alice - } - - void SSUSession::SendKeepAlive () - { - if (m_State == eSessionStateEstablished) - { - uint8_t buf[48 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 0; // flags - payload++; - *payload = 0; // num fragments - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); - Send (buf, 48); - LogPrint (eLogDebug, "SSU: keep-alive sent"); - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - } - } - - void SSUSession::SendSessionDestroyed () - { - if (m_IsSessionKey) - { - uint8_t buf[48 + 18] = {0}; - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); - try - { - Send (buf, 48); - } - catch (std::exception& ex) - { - LogPrint (eLogWarning, "SSU: Exception while sending session destoroyed: ", ex.what ()); - } - LogPrint (eLogDebug, "SSU: Session destroyed sent"); - } - } - - void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) - { - uint8_t buf[SSU_MTU_V4 + 18] = {0}; - size_t msgSize = len + sizeof (SSUHeader); - size_t paddingSize = msgSize & 0x0F; // %16 - if (paddingSize > 0) msgSize += (16 - paddingSize); - if (msgSize > SSU_MTU_V4) - { - LogPrint (eLogWarning, "SSU: Payload size ", msgSize, " exceeds MTU"); - return; - } - memcpy (buf + sizeof (SSUHeader), payload, len); - // encrypt message with session key - FillHeaderAndEncrypt (type, buf, msgSize); - Send (buf, msgSize); - } - - void SSUSession::Send (const uint8_t * buf, size_t size) - { - m_NumSentBytes += size; - i2p::transport::transports.UpdateSentBytes (size); - m_Server.Send (buf, size, m_RemoteEndpoint); - } - - size_t SSUSession::ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port) - { - if (!len) return 0; - uint8_t size = *buf; - size_t s = 1 + size + 2; // size + address + port - if (len < s) - { - LogPrint (eLogWarning, "SSU: Address is too short ", len); - port = 0; - return len; - } - buf++; // size - if (size == 4) - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), buf, 4); - ip = boost::asio::ip::address_v4 (bytes); - } - else if (size == 16) - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), buf, 16); - ip = boost::asio::ip::address_v6 (bytes); - } - else - LogPrint (eLogWarning, "SSU: Address size ", int(size), " is not supported"); - buf += size; - port = bufbe16toh (buf); - return s; - } -} + void SSUSession::ScheduleConnectTimer() { + m_ConnectTimer.cancel(); + m_ConnectTimer.expires_from_now(boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_ConnectTimer.async_wait(std::bind(&SSUSession::HandleConnectTimer, + shared_from_this(), std::placeholders::_1)); + } + + void SSUSession::HandleConnectTimer(const boost::system::error_code &ecode) { + if (!ecode) { + // timeout expired + LogPrint(eLogWarning, "SSU: Session with ", m_RemoteEndpoint, " was not established after ", + SSU_CONNECT_TIMEOUT, " seconds"); + Failed(); + } + } + + void SSUSession::Introduce(const i2p::data::RouterInfo::Introducer &introducer, + std::shared_ptr to) { + if (m_State == eSessionStateUnknown) { + // set connect timer + m_ConnectTimer.expires_from_now(boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_ConnectTimer.async_wait(std::bind(&SSUSession::HandleConnectTimer, + shared_from_this(), std::placeholders::_1)); + } + uint32_t nonce; + RAND_bytes((uint8_t * ) & nonce, 4); + auto ts = i2p::util::GetSecondsSinceEpoch(); + m_RelayRequests.emplace(nonce, std::make_pair(to, ts)); + SendRelayRequest(introducer, nonce); + } + + void SSUSession::WaitForIntroduction() { + m_State = eSessionStateIntroduced; + // set connect timer + m_ConnectTimer.expires_from_now(boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_ConnectTimer.async_wait(std::bind(&SSUSession::HandleConnectTimer, + shared_from_this(), std::placeholders::_1)); + } + + void SSUSession::Close() { + SendSessionDestroyed(); + Reset(); + m_State = eSessionStateClosed; + } + + void SSUSession::Reset() { + m_State = eSessionStateUnknown; + transports.PeerDisconnected(shared_from_this()); + m_Data.Stop(); + m_ConnectTimer.cancel(); + if (m_SentRelayTag) { + m_Server.RemoveRelay(m_SentRelayTag); // relay tag is not valid anymore + m_SentRelayTag = 0; + } + m_DHKeysPair = nullptr; + m_SignedData = nullptr; + m_IsSessionKey = false; + } + + void SSUSession::Done() { + GetService().post(std::bind(&SSUSession::Failed, shared_from_this())); + } + + void SSUSession::Established() { + m_State = eSessionStateEstablished; + m_DHKeysPair = nullptr; + m_SignedData = nullptr; + m_Data.Start(); + transports.PeerConnected(shared_from_this()); + if (m_IsPeerTest) + SendPeerTest(); + if (m_SentRelayTag) + m_Server.AddRelay(m_SentRelayTag, shared_from_this()); + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + } + + void SSUSession::Failed() { + if (m_State != eSessionStateFailed) { + m_State = eSessionStateFailed; + m_Server.DeleteSession(shared_from_this()); + } + } + + void SSUSession::SendI2NPMessages(const std::vector > &msgs) { + GetService().post(std::bind(&SSUSession::PostI2NPMessages, shared_from_this(), msgs)); + } + + void SSUSession::PostI2NPMessages(std::vector > msgs) { + if (m_State == eSessionStateEstablished) { + for (const auto &it: msgs) + if (it) { + if (it->GetLength() <= SSU_MAX_I2NP_MESSAGE_SIZE) + m_Data.Send(it); + else + LogPrint(eLogError, "SSU: I2NP message of size ", it->GetLength(), + " can't be sent. Dropped"); + } + } + } + + void SSUSession::ProcessData(uint8_t *buf, size_t len) { + m_Data.ProcessMessage(buf, len); + m_IsDataReceived = true; + } + + void SSUSession::FlushData() { + if (m_IsDataReceived) { + m_Data.FlushReceivedMessage(); + m_IsDataReceived = false; + } + } + + void SSUSession::CleanUp(uint64_t ts) { + m_Data.CleanUp(ts); + for (auto it = m_RelayRequests.begin(); it != m_RelayRequests.end();) { + if (ts > it->second.second + SSU_CONNECT_TIMEOUT) + it = m_RelayRequests.erase(it); + else + ++it; + } + } + + void SSUSession::ProcessPeerTest(const uint8_t *buf, size_t len, + const boost::asio::ip::udp::endpoint &senderEndpoint) { + uint32_t nonce = bufbe32toh(buf); // 4 bytes + boost::asio::ip::address addr; // Alice's address + uint16_t port = 0; // and port + auto size = ExtractIPAddressAndPort(buf + 4, len - 4, addr, port); + if (port && (size != 7) && (size != 19)) { + LogPrint(eLogWarning, "SSU: Address of ", size - 3, " bytes not supported"); + return; + } + const uint8_t *introKey = buf + 4 + size; + switch (m_Server.GetPeerTestParticipant(nonce)) { + // existing test + case ePeerTestParticipantAlice1: { + if (m_Server.GetPeerTestSession(nonce) == shared_from_this()) // Alice-Bob + { + LogPrint(eLogDebug, "SSU: Peer test from Bob. We are Alice"); + if (IsV6()) { + if (i2p::context.GetStatusV6() == eRouterStatusTesting) { + i2p::context.SetStatusV6(eRouterStatusFirewalled); + m_Server.RescheduleIntroducersUpdateTimerV6(); + } + } else if (i2p::context.GetStatus() == eRouterStatusTesting) // still not OK + { + i2p::context.SetStatus(eRouterStatusFirewalled); + m_Server.RescheduleIntroducersUpdateTimer(); + } + } else { + LogPrint(eLogDebug, "SSU: First peer test from Charlie. We are Alice"); + if (m_State == eSessionStateEstablished) + LogPrint(eLogWarning, + "SSU: First peer test from Charlie through established session. We are Alice"); + if (IsV6()) + i2p::context.SetStatusV6(eRouterStatusOK); + else + i2p::context.SetStatus(eRouterStatusOK); + m_Server.UpdatePeerTest(nonce, ePeerTestParticipantAlice2); + SendPeerTest(nonce, senderEndpoint.address(), senderEndpoint.port(), introKey, true, + false); // to Charlie + } + break; + } + case ePeerTestParticipantAlice2: { + if (m_Server.GetPeerTestSession(nonce) == shared_from_this()) // Alice-Bob + LogPrint(eLogDebug, "SSU: Peer test from Bob. We are Alice"); + else { + // peer test successive + LogPrint(eLogDebug, "SSU: Second peer test from Charlie. We are Alice"); + if (IsV6()) + i2p::context.SetStatusV6(eRouterStatusOK); + else + i2p::context.SetStatus(eRouterStatusOK); + m_Server.RemovePeerTest(nonce); + } + break; + } + case ePeerTestParticipantBob: { + LogPrint(eLogDebug, "SSU: Peer test from Charlie. We are Bob"); + auto session = m_Server.GetPeerTestSession(nonce); // session with Alice from PeerTest + if (session && session->m_State == eSessionStateEstablished) { + const auto &ep = session->GetRemoteEndpoint(); // Alice's endpoint as known to Bob + session->SendPeerTest(nonce, ep.address(), ep.port(), introKey, false, + true); // send back to Alice + } + m_Server.RemovePeerTest(nonce); // nonce has been used + break; + } + case ePeerTestParticipantCharlie: { + LogPrint(eLogDebug, "SSU: Peer test from Alice. We are Charlie"); + SendPeerTest(nonce, senderEndpoint.address(), senderEndpoint.port(), + introKey); // to Alice with her actual address + m_Server.RemovePeerTest(nonce); // nonce has been used + break; + } + // test not found + case ePeerTestParticipantUnknown: { + if (m_State == eSessionStateEstablished) { + // new test + if (port) { + LogPrint(eLogDebug, "SSU: Peer test from Bob. We are Charlie"); + Send(PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob + if (!addr.is_unspecified() && !i2p::util::net::IsInReservedRange(addr)) { + m_Server.NewPeerTest(nonce, ePeerTestParticipantCharlie); + SendPeerTest(nonce, addr, port, + introKey); // to Alice with her address received from Bob + } + } else { + LogPrint(eLogDebug, "SSU: Peer test from Alice. We are Bob"); + auto session = senderEndpoint.address().is_v4() ? m_Server.GetRandomEstablishedV4Session( + shared_from_this()) : m_Server.GetRandomEstablishedV6Session( + shared_from_this()); // Charlie + if (session) { + m_Server.NewPeerTest(nonce, ePeerTestParticipantBob, shared_from_this()); + session->SendPeerTest(nonce, senderEndpoint.address(), senderEndpoint.port(), introKey, + false); // to Charlie with Alice's actual address + } + } + } else + LogPrint(eLogError, "SSU: Unexpected peer test"); + } + } + } + + void SSUSession::SendPeerTest(uint32_t nonce, const boost::asio::ip::address &address, uint16_t port, + const uint8_t *introKey, bool toAddress, bool sendAddress) + // toAddress is true for Alice<->Chalie communications only + // sendAddress is false if message comes from Alice + { + uint8_t buf[80 + 18] = {0}; + uint8_t iv[16]; + uint8_t *payload = buf + sizeof(SSUHeader); + htobe32buf(payload, nonce); + payload += 4; // nonce + // address and port + if (sendAddress) { + if (address.is_v4()) { + *payload = 4; + memcpy(payload + 1, address.to_v4().to_bytes().data(), 4); // our IP V4 + } else if (address.is_v6()) { + *payload = 16; + memcpy(payload + 1, address.to_v6().to_bytes().data(), 16); // our IP V6 + } else + *payload = 0; + payload += (payload[0] + 1); + } else { + *payload = 0; + payload++; //size + } + htobe16buf(payload, port); + payload += 2; // port + // intro key + if (toAddress) { + // send our intro key to address instead of its own + auto addr = address.is_v4() ? i2p::context.GetRouterInfo().GetSSUAddress(true) : // ipv4 + i2p::context.GetRouterInfo().GetSSUV6Address(); + if (addr) + memcpy(payload, addr->i, 32); // intro key + else + LogPrint(eLogInfo, "SSU: SSU is not supported. Can't send peer test"); + } else + memcpy(payload, introKey, 32); // intro key + + // send + RAND_bytes(iv, 16); // random iv + if (toAddress) { + // encrypt message with specified intro key + FillHeaderAndEncrypt(PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); + boost::asio::ip::udp::endpoint e(address, port); + m_Server.Send(buf, 80, e); + } else { + // encrypt message with session key + FillHeaderAndEncrypt(PAYLOAD_TYPE_PEER_TEST, buf, 80); + Send(buf, 80); + } + } + + void SSUSession::SendPeerTest() { + // we are Alice + LogPrint(eLogDebug, "SSU: Sending peer test"); + auto address = IsV6() ? i2p::context.GetRouterInfo().GetSSUV6Address() + : i2p::context.GetRouterInfo().GetSSUAddress(true); + if (!address) { + LogPrint(eLogInfo, "SSU: SSU is not supported. Can't send peer test"); + return; + } + uint32_t nonce; + RAND_bytes((uint8_t * ) & nonce, 4); + if (!nonce) nonce = 1; + m_IsPeerTest = false; + m_Server.NewPeerTest(nonce, ePeerTestParticipantAlice1, shared_from_this()); + SendPeerTest(nonce, boost::asio::ip::address(), 0, address->i, false, + false); // address and port always zero for Alice + } + + void SSUSession::SendKeepAlive() { + if (m_State == eSessionStateEstablished) { + uint8_t buf[48 + 18] = {0}; + uint8_t *payload = buf + sizeof(SSUHeader); + *payload = 0; // flags + payload++; + *payload = 0; // num fragments + // encrypt message with session key + FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, 48); + Send(buf, 48); + LogPrint(eLogDebug, "SSU: keep-alive sent"); + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch(); + } + } + + void SSUSession::SendSessionDestroyed() { + if (m_IsSessionKey) { + uint8_t buf[48 + 18] = {0}; + // encrypt message with session key + FillHeaderAndEncrypt(PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); + try { + Send(buf, 48); + } + catch (std::exception &ex) { + LogPrint(eLogWarning, "SSU: Exception while sending session destoroyed: ", ex.what()); + } + LogPrint(eLogDebug, "SSU: Session destroyed sent"); + } + } + + void SSUSession::Send(uint8_t type, const uint8_t *payload, size_t len) { + uint8_t buf[SSU_MTU_V4 + 18] = {0}; + size_t msgSize = len + sizeof(SSUHeader); + size_t paddingSize = msgSize & 0x0F; // %16 + if (paddingSize > 0) msgSize += (16 - paddingSize); + if (msgSize > SSU_MTU_V4) { + LogPrint(eLogWarning, "SSU: Payload size ", msgSize, " exceeds MTU"); + return; + } + memcpy(buf + sizeof(SSUHeader), payload, len); + // encrypt message with session key + FillHeaderAndEncrypt(type, buf, msgSize); + Send(buf, msgSize); + } + + void SSUSession::Send(const uint8_t *buf, size_t size) { + m_NumSentBytes += size; + i2p::transport::transports.UpdateSentBytes(size); + m_Server.Send(buf, size, m_RemoteEndpoint); + } + + size_t SSUSession::ExtractIPAddressAndPort(const uint8_t *buf, size_t len, boost::asio::ip::address &ip, + uint16_t &port) { + if (!len) return 0; + uint8_t size = *buf; + size_t s = 1 + size + 2; // size + address + port + if (len < s) { + LogPrint(eLogWarning, "SSU: Address is too short ", len); + port = 0; + return len; + } + buf++; // size + if (size == 4) { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy(bytes.data(), buf, 4); + ip = boost::asio::ip::address_v4(bytes); + } else if (size == 16) { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy(bytes.data(), buf, 16); + ip = boost::asio::ip::address_v6(bytes); + } else + LogPrint(eLogWarning, "SSU: Address size ", int(size), " is not supported"); + buf += size; + port = bufbe16toh(buf); + return s; + } + } } diff --git a/libi2pd/SSUSession.h b/libi2pd/SSUSession.h index e28b4991..48a73d2d 100644 --- a/libi2pd/SSUSession.h +++ b/libi2pd/SSUSession.h @@ -17,161 +17,203 @@ #include "TransportSession.h" #include "SSUData.h" -namespace i2p -{ -namespace transport -{ - 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]; +namespace i2p { + namespace transport { + const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04; - uint8_t GetPayloadType () const { return flag >> 4; }; - bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; }; - }; + struct SSUHeader { + uint8_t mac[16]; + uint8_t iv[16]; + uint8_t flag; + uint8_t time[4]; - const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds - const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes - const int SSU_CLOCK_SKEW = 60; // in seconds - const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust - const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768; + uint8_t GetPayloadType() const { return flag >> 4; }; - // payload types (4 bits) - 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; + bool IsExtendedOptions() const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; }; + }; - // extended options - const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001; + const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds + const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU_CLOCK_SKEW = 60; // in seconds + const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust + const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768; - enum SessionState - { - eSessionStateUnknown, - eSessionStateIntroduced, - eSessionStateEstablished, - eSessionStateClosed, - eSessionStateFailed - }; + // payload types (4 bits) + 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; - enum PeerTestParticipant - { - ePeerTestParticipantUnknown = 0, - ePeerTestParticipantAlice1, - ePeerTestParticipantAlice2, - ePeerTestParticipantBob, - ePeerTestParticipantCharlie - }; + // extended options + const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001; - class SSUServer; - class SSUSession: public TransportSession, public std::enable_shared_from_this - { - public: + enum SessionState { + eSessionStateUnknown, + eSessionStateIntroduced, + eSessionStateEstablished, + eSessionStateClosed, + eSessionStateFailed + }; - SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr router = nullptr, bool peerTest = false); - void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - ~SSUSession (); + enum PeerTestParticipant { + ePeerTestParticipantUnknown = 0, + ePeerTestParticipantAlice1, + ePeerTestParticipantAlice2, + ePeerTestParticipantBob, + ePeerTestParticipantCharlie + }; - void Connect (); - void WaitForConnect (); - void Introduce (const i2p::data::RouterInfo::Introducer& introducer, - std::shared_ptr 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; }; + class SSUServer; - bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; - void SendI2NPMessages (const std::vector >& msgs); - void SendPeerTest (); // Alice + class SSUSession : public TransportSession, public std::enable_shared_from_this { + public: - SessionState GetState () const { return m_State; }; - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + SSUSession(SSUServer &server, boost::asio::ip::udp::endpoint &remoteEndpoint, + std::shared_ptr router = nullptr, bool peerTest = false); - void SendKeepAlive (); - uint32_t GetRelayTag () const { return m_RelayTag; }; - const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; }; + void ProcessNextMessage(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint); - void FlushData (); - void CleanUp (uint64_t ts); + ~SSUSession(); - private: + void Connect(); - boost::asio::io_service& GetService (); - void CreateAESandMacKey (const uint8_t * pubKey); - size_t GetSSUHeaderSize (const uint8_t * buf) const; - void PostI2NPMessages (std::vector > 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 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 WaitForConnect(); - 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 Introduce(const i2p::data::RouterInfo::Introducer &introducer, + std::shared_ptr to); // Alice to Charlie + void WaitForIntroduction(); - 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 - 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 m_SignedData; // we need it for SessionConfirmed only - std::map, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp) - std::shared_ptr m_DHKeysPair; // X - for client and Y - for server - }; -} + const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() { return m_RemoteEndpoint; }; + + SSUServer &GetServer() { return m_Server; }; + + bool IsV6() const { return m_RemoteEndpoint.address().is_v6(); }; + + void SendI2NPMessages(const std::vector > &msgs); + + void SendPeerTest(); // Alice + + SessionState GetState() const { return m_State; }; + + size_t GetNumSentBytes() const { return m_NumSentBytes; }; + + size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; }; + + void SendKeepAlive(); + + 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 > 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 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 m_SignedData; // we need it for SessionConfirmed only + std::map, uint64_t> > m_RelayRequests; // nonce->(Charlie, timestamp) + std::shared_ptr m_DHKeysPair; // X - for client and Y - for server + }; + } } #endif diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index ebc188a9..413dfe68 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -10,131 +10,124 @@ #include "Log.h" #include "Signature.h" -namespace i2p -{ -namespace crypto -{ +namespace i2p { + namespace crypto { #if OPENSSL_EDDSA - EDDSA25519Verifier::EDDSA25519Verifier () - { - m_MDCtx = EVP_MD_CTX_create (); - } + EDDSA25519Verifier::EDDSA25519Verifier () + { + m_MDCtx = EVP_MD_CTX_create (); + } - EDDSA25519Verifier::~EDDSA25519Verifier () - { - EVP_MD_CTX_destroy (m_MDCtx); - } + EDDSA25519Verifier::~EDDSA25519Verifier () + { + EVP_MD_CTX_destroy (m_MDCtx); + } - void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) - { - EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); - EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey); - EVP_PKEY_free (pkey); - } + void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) + { + EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); + EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey); + EVP_PKEY_free (pkey); + } - bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); - } + bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); + } #else - EDDSA25519Verifier::EDDSA25519Verifier () - { - } - EDDSA25519Verifier::~EDDSA25519Verifier () - { - } + EDDSA25519Verifier::EDDSA25519Verifier() { + } - void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) - { - 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); - } + EDDSA25519Verifier::~EDDSA25519Verifier() { + } - bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - 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); + void EDDSA25519Verifier::SetPublicKey(const uint8_t *signingKey) { + 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 { + 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 - EDDSA25519SignerCompat::EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - { - // expand key - Ed25519::ExpandPrivateKey (signingPrivateKey, m_ExpandedPrivateKey); - // generate and encode public key - BN_CTX * ctx = BN_CTX_new (); - auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); - GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); + EDDSA25519SignerCompat::EDDSA25519SignerCompat(const uint8_t *signingPrivateKey, + const uint8_t *signingPublicKey) { + // expand key + Ed25519::ExpandPrivateKey(signingPrivateKey, m_ExpandedPrivateKey); + // generate and encode public key + BN_CTX *ctx = BN_CTX_new(); + auto publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx); + GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx); - if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) - { - // keys don't match, it means older key with 0x1F - LogPrint (eLogWarning, "Older EdDSA key detected"); - m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit - publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); - GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); - } - BN_CTX_free (ctx); - } + if (signingPublicKey && memcmp(m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) { + // keys don't match, it means older key with 0x1F + LogPrint(eLogWarning, "Older EdDSA key detected"); + m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit + publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx); + GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx); + } + BN_CTX_free(ctx); + } - EDDSA25519SignerCompat::~EDDSA25519SignerCompat () - { - } + EDDSA25519SignerCompat::~EDDSA25519SignerCompat() { + } - void EDDSA25519SignerCompat::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature); - } + void EDDSA25519SignerCompat::Sign(const uint8_t *buf, int len, uint8_t *signature) const { + GetEd25519()->Sign(m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature); + } #if OPENSSL_EDDSA - EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): - m_MDCtx (nullptr), m_Fallback (nullptr) - { - EVP_PKEY * pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); - uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; - size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_raw_public_key (pkey, publicKey, &len); - if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) - { - LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); - m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); - } - else - { - m_MDCtx = EVP_MD_CTX_create (); - EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey); - } - EVP_PKEY_free (pkey); - } + EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): + m_MDCtx (nullptr), m_Fallback (nullptr) + { + EVP_PKEY * pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); + uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; + size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; + EVP_PKEY_get_raw_public_key (pkey, publicKey, &len); + if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) + { + LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); + m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); + } + else + { + m_MDCtx = EVP_MD_CTX_create (); + EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey); + } + EVP_PKEY_free (pkey); + } - EDDSA25519Signer::~EDDSA25519Signer () - { - if (m_Fallback) delete m_Fallback; - EVP_MD_CTX_destroy (m_MDCtx); - } + EDDSA25519Signer::~EDDSA25519Signer () + { + if (m_Fallback) delete m_Fallback; + EVP_MD_CTX_destroy (m_MDCtx); + } - void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - if (m_Fallback) return m_Fallback->Sign (buf, len, signature); - else - { - size_t l = 64; - uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 - EVP_DigestSign (m_MDCtx, sig, &l, buf, len); - memcpy (signature, sig, 64); - } - } + void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const + { + if (m_Fallback) return m_Fallback->Sign (buf, len, signature); + else + { + size_t l = 64; + uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 + EVP_DigestSign (m_MDCtx, sig, &l, buf, len); + memcpy (signature, sig, 64); + } + } #endif -} + } } diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h index e153e66d..f89c413c 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -19,518 +19,511 @@ #include "Ed25519.h" #include "Gost.h" -namespace i2p -{ -namespace crypto -{ - class Verifier - { - public: +namespace i2p { + namespace crypto { + class Verifier { + public: - 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; - }; + virtual ~Verifier() {}; - class Signer - { - public: + virtual bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const = 0; - virtual ~Signer () {}; - virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0; - }; + virtual size_t GetPublicKeyLen() const = 0; - 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; - class DSAVerifier: public Verifier - { - public: + virtual size_t GetSignatureLen() const = 0; - DSAVerifier () - { - m_PublicKey = CreateDSA (); - } + virtual size_t GetPrivateKeyLen() const { return GetSignatureLen() / 2; }; - void SetPublicKey (const uint8_t * signingKey) - { - DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL); - } + virtual void SetPublicKey(const uint8_t *signingKey) = 0; + }; - ~DSAVerifier () - { - DSA_free (m_PublicKey); - } + class Signer { + public: - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - // 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; - } + virtual ~Signer() {}; - size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; }; + virtual void Sign(const uint8_t *buf, int len, uint8_t *signature) const = 0; + }; - 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 - { - public: + DSAVerifier() { + m_PublicKey = CreateDSA(); + } - DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - // openssl 1.1 always requires DSA public key even for signing - { - m_PrivateKey = CreateDSA (); - DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL)); - } + void SetPublicKey(const uint8_t *signingKey) { + DSA_set0_key(m_PublicKey, BN_bin2bn(signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL); + } - ~DSASigner () - { - DSA_free (m_PrivateKey); - } + ~DSAVerifier() { + DSA_free(m_PublicKey); + } - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[20]; - SHA1 (buf, len, digest); - DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey); - 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); - } + bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const { + // 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; + } - 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) - { - 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); - } + private: - struct SHA256Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - SHA256 (buf, len, digest); - } + DSA *m_PublicKey; + }; - enum { hashLen = 32 }; - }; + class DSASigner : public Signer { + public: - struct SHA384Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - SHA384 (buf, len, digest); - } + DSASigner(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey) + // openssl 1.1 always requires DSA public key even for signing + { + m_PrivateKey = CreateDSA(); + 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 - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - SHA512 (buf, len, digest); - } + void Sign(const uint8_t *buf, int len, uint8_t *signature) const { + uint8_t digest[20]; + SHA1(buf, len, digest); + DSA_SIG *sig = DSA_do_sign(digest, 20, m_PrivateKey); + 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 - template - class ECDSAVerifier: public Verifier - { - public: + DSA *m_PrivateKey; + }; - ECDSAVerifier () - { - m_PublicKey = EC_KEY_new_by_curve_name (curve); - } + inline void CreateDSARandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { + 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); + } - 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); - } + struct SHA256Hash { + static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) { + SHA256(buf, len, digest); + } - ~ECDSAVerifier () - { - EC_KEY_free (m_PublicKey); - } + enum { + hashLen = 32 + }; + }; - 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; - } + struct SHA384Hash { + static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) { + SHA384(buf, len, digest); + } - size_t GetPublicKeyLen () const { return keyLen; }; - size_t GetSignatureLen () const { return keyLen; }; // signature length = key length + enum { + 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 + 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 - class ECDSASigner: public Signer - { - public: + template + class ECDSASigner : public Signer { + public: - ECDSASigner (const uint8_t * signingPrivateKey) - { - m_PrivateKey = EC_KEY_new_by_curve_name (curve); - EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL)); - } + ECDSASigner(const uint8_t *signingPrivateKey) { + m_PrivateKey = EC_KEY_new_by_curve_name(curve); + EC_KEY_set_private_key(m_PrivateKey, BN_bin2bn(signingPrivateKey, keyLen / 2, NULL)); + } - ~ECDSASigner () - { - EC_KEY_free (m_PrivateKey); - } + ~ECDSASigner() { + EC_KEY_free(m_PrivateKey); + } - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[Hash::hashLen]; - Hash::CalculateHash (buf, len, digest); - ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey); - const BIGNUM * r, * s; - ECDSA_SIG_get0 (sig, &r, &s); - // signatureLen = keyLen - bn2buf (r, signature, keyLen/2); - bn2buf (s, signature + keyLen/2, keyLen/2); - ECDSA_SIG_free(sig); - } + void Sign(const uint8_t *buf, int len, uint8_t *signature) const { + uint8_t digest[Hash::hashLen]; + Hash::CalculateHash(buf, len, digest); + ECDSA_SIG *sig = ECDSA_do_sign(digest, Hash::hashLen, m_PrivateKey); + const BIGNUM *r, *s; + ECDSA_SIG_get0(sig, &r, &s); + // signatureLen = keyLen + bn2buf(r, signature, keyLen / 2); + bn2buf(s, signature + keyLen / 2, keyLen / 2); + 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) - { - EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve); - EC_KEY_generate_key (signingKey); - bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2); - BIGNUM * x = BN_new(), * y = BN_new(); - EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey), - EC_KEY_get0_public_key (signingKey), x, y, NULL); - bn2buf (x, signingPublicKey, keyLen/2); - bn2buf (y, signingPublicKey + keyLen/2, keyLen/2); - BN_free (x); BN_free (y); - EC_KEY_free (signingKey); - } + 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_generate_key(signingKey); + bn2buf(EC_KEY_get0_private_key(signingKey), signingPrivateKey, keyLen / 2); + BIGNUM *x = BN_new(), *y = BN_new(); + EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(signingKey), + EC_KEY_get0_public_key(signingKey), x, y, NULL); + bn2buf(x, signingPublicKey, keyLen / 2); + bn2buf(y, signingPublicKey + keyLen / 2, keyLen / 2); + BN_free(x); + BN_free(y); + EC_KEY_free(signingKey); + } // ECDSA_SHA256_P256 - const size_t ECDSAP256_KEY_LENGTH = 64; - typedef ECDSAVerifier ECDSAP256Verifier; - typedef ECDSASigner ECDSAP256Signer; + const size_t ECDSAP256_KEY_LENGTH = 64; + typedef ECDSAVerifier ECDSAP256Verifier; + typedef ECDSASigner ECDSAP256Signer; - inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } + inline void CreateECDSAP256RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { + CreateECDSARandomKeys(NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } // ECDSA_SHA384_P384 - const size_t ECDSAP384_KEY_LENGTH = 96; - typedef ECDSAVerifier ECDSAP384Verifier; - typedef ECDSASigner ECDSAP384Signer; + const size_t ECDSAP384_KEY_LENGTH = 96; + typedef ECDSAVerifier ECDSAP384Verifier; + typedef ECDSASigner ECDSAP384Signer; - inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } + inline void CreateECDSAP384RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { + CreateECDSARandomKeys(NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } // ECDSA_SHA512_P521 - const size_t ECDSAP521_KEY_LENGTH = 132; - typedef ECDSAVerifier ECDSAP521Verifier; - typedef ECDSASigner ECDSAP521Signer; + const size_t ECDSAP521_KEY_LENGTH = 132; + typedef ECDSAVerifier ECDSAP521Verifier; + typedef ECDSASigner ECDSAP521Signer; - inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } + inline void CreateECDSAP521RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { + CreateECDSARandomKeys(NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } - // EdDSA - class EDDSA25519Verifier: public Verifier - { - public: + // EdDSA + class EDDSA25519Verifier : public Verifier { + public: - EDDSA25519Verifier (); - void SetPublicKey (const uint8_t * signingKey); - ~EDDSA25519Verifier (); + 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; }; - size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; }; + ~EDDSA25519Verifier(); - 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 - EVP_MD_CTX * m_MDCtx; + EVP_MD_CTX * m_MDCtx; #else - EDDSAPoint m_PublicKey; - uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; + EDDSAPoint m_PublicKey; + uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; #endif - }; + }; - class EDDSA25519SignerCompat: public Signer - { - public: + class EDDSA25519SignerCompat : public Signer { + public: - EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); - // we pass signingPublicKey to check if it matches private key - ~EDDSA25519SignerCompat (); + EDDSA25519SignerCompat(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey = nullptr); - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation + // we pass signingPublicKey to check if it matches private key + ~EDDSA25519SignerCompat(); - private: + void Sign(const uint8_t *buf, int len, uint8_t *signature) const; - uint8_t m_ExpandedPrivateKey[64]; - uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; - }; + const uint8_t *GetPublicKey() const { return m_PublicKeyEncoded; }; // for keys creation + + private: + + uint8_t m_ExpandedPrivateKey[64]; + uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; + }; #if OPENSSL_EDDSA - class EDDSA25519Signer: public Signer - { - public: + class EDDSA25519Signer: public Signer + { + public: - EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); - // we pass signingPublicKey to check if it matches private key - ~EDDSA25519Signer (); + EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); + // we pass signingPublicKey to check if it matches private key + ~EDDSA25519Signer (); - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; + void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - private: + private: - EVP_MD_CTX * m_MDCtx; - EDDSA25519SignerCompat * m_Fallback; - }; + EVP_MD_CTX * m_MDCtx; + EDDSA25519SignerCompat * m_Fallback; + }; #else - typedef EDDSA25519SignerCompat EDDSA25519Signer; + typedef EDDSA25519SignerCompat EDDSA25519Signer; #endif - inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { + inline void CreateEDDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { #if OPENSSL_EDDSA - EVP_PKEY *pkey = NULL; - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL); - EVP_PKEY_keygen_init (pctx); - EVP_PKEY_keygen (pctx, &pkey); - EVP_PKEY_CTX_free (pctx); - size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_raw_public_key (pkey, signingPublicKey, &len); - len = EDDSA25519_PRIVATE_KEY_LENGTH; - EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len); - EVP_PKEY_free (pkey); + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL); + EVP_PKEY_keygen_init (pctx); + EVP_PKEY_keygen (pctx, &pkey); + EVP_PKEY_CTX_free (pctx); + size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; + EVP_PKEY_get_raw_public_key (pkey, signingPublicKey, &len); + len = EDDSA25519_PRIVATE_KEY_LENGTH; + EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len); + EVP_PKEY_free (pkey); #else - RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); - EDDSA25519Signer signer (signingPrivateKey); - memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); + RAND_bytes(signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); + EDDSA25519Signer signer(signingPrivateKey); + memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH); #endif - } + } - // ГОСТ Р 34.11 - struct GOSTR3411_256_Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - GOSTR3411_2012_256 (buf, len, digest); - } + // ГОСТ Р 34.11 + struct GOSTR3411_256_Hash { + static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) { + GOSTR3411_2012_256(buf, len, digest); + } - enum { hashLen = 32 }; - }; + enum { + hashLen = 32 + }; + }; - struct GOSTR3411_512_Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - GOSTR3411_2012_512 (buf, len, digest); - } + struct GOSTR3411_512_Hash { + static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) { + GOSTR3411_2012_512(buf, len, digest); + } - enum { hashLen = 64 }; - }; + enum { + hashLen = 64 + }; + }; - // ГОСТ Р 34.10 - const size_t GOSTR3410_256_PUBLIC_KEY_LENGTH = 64; - const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128; + // ГОСТ Р 34.10 + const size_t GOSTR3410_256_PUBLIC_KEY_LENGTH = 64; + const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128; - template - class GOSTR3410Verifier: public Verifier - { - public: + template + class GOSTR3410Verifier : public Verifier { + public: - enum { keyLen = Hash::hashLen }; + enum { + keyLen = Hash::hashLen + }; - GOSTR3410Verifier (GOSTR3410ParamSet paramSet): - m_ParamSet (paramSet), m_PublicKey (nullptr) - { - } + GOSTR3410Verifier(GOSTR3410ParamSet paramSet) : + m_ParamSet(paramSet), m_PublicKey(nullptr) { + } - void SetPublicKey (const uint8_t * signingKey) - { - BIGNUM * x = BN_bin2bn (signingKey, GetPublicKeyLen ()/2, NULL); - BIGNUM * y = BN_bin2bn (signingKey + GetPublicKeyLen ()/2, GetPublicKeyLen ()/2, NULL); - m_PublicKey = GetGOSTR3410Curve (m_ParamSet)->CreatePoint (x, y); - BN_free (x); BN_free (y); - } - ~GOSTR3410Verifier () - { - if (m_PublicKey) EC_POINT_free (m_PublicKey); - } + void SetPublicKey(const uint8_t *signingKey) { + BIGNUM *x = BN_bin2bn(signingKey, GetPublicKeyLen() / 2, NULL); + BIGNUM *y = BN_bin2bn(signingKey + GetPublicKeyLen() / 2, GetPublicKeyLen() / 2, NULL); + m_PublicKey = GetGOSTR3410Curve(m_ParamSet)->CreatePoint(x, y); + BN_free(x); + BN_free(y); + } - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - 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; - } + ~GOSTR3410Verifier() { + if (m_PublicKey) EC_POINT_free(m_PublicKey); + } - size_t GetPublicKeyLen () const { return keyLen*2; } - size_t GetSignatureLen () const { return keyLen*2; } + bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const { + 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; - EC_POINT * m_PublicKey; - }; + size_t GetSignatureLen() const { return keyLen * 2; } - template - class GOSTR3410Signer: public Signer - { - public: + private: - enum { keyLen = Hash::hashLen }; + GOSTR3410ParamSet m_ParamSet; + EC_POINT *m_PublicKey; + }; - GOSTR3410Signer (GOSTR3410ParamSet paramSet, const uint8_t * signingPrivateKey): - m_ParamSet (paramSet) - { - m_PrivateKey = BN_bin2bn (signingPrivateKey, keyLen, nullptr); - } - ~GOSTR3410Signer () { BN_free (m_PrivateKey); } + template + class GOSTR3410Signer : public Signer { + public: - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - 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); - } + enum { + keyLen = Hash::hashLen + }; - private: + GOSTR3410Signer(GOSTR3410ParamSet paramSet, const uint8_t *signingPrivateKey) : + m_ParamSet(paramSet) { + m_PrivateKey = BN_bin2bn(signingPrivateKey, keyLen, nullptr); + } - GOSTR3410ParamSet m_ParamSet; - BIGNUM * m_PrivateKey; - }; + ~GOSTR3410Signer() { BN_free(m_PrivateKey); } - inline void CreateGOSTR3410RandomKeys (GOSTR3410ParamSet paramSet, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - const auto& curve = GetGOSTR3410Curve (paramSet); - auto keyLen = curve->GetKeyLen (); - RAND_bytes (signingPrivateKey, keyLen); - BIGNUM * priv = BN_bin2bn (signingPrivateKey, keyLen, nullptr); + void Sign(const uint8_t *buf, int len, uint8_t *signature) const { + 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); + } - auto pub = curve->MulP (priv); - 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); - } + private: - typedef GOSTR3410Verifier GOSTR3410_256_Verifier; - typedef GOSTR3410Signer GOSTR3410_256_Signer; - typedef GOSTR3410Verifier GOSTR3410_512_Verifier; - typedef GOSTR3410Signer GOSTR3410_512_Signer; + GOSTR3410ParamSet m_ParamSet; + BIGNUM *m_PrivateKey; + }; - // RedDSA - typedef EDDSA25519Verifier RedDSA25519Verifier; - class RedDSA25519Signer: public Signer - { - public: + inline void + CreateGOSTR3410RandomKeys(GOSTR3410ParamSet paramSet, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { + const auto &curve = GetGOSTR3410Curve(paramSet); + auto keyLen = curve->GetKeyLen(); + RAND_bytes(signingPrivateKey, keyLen); + BIGNUM *priv = BN_bin2bn(signingPrivateKey, keyLen, nullptr); - RedDSA25519Signer (const uint8_t * signingPrivateKey) - { - 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); - } - ~RedDSA25519Signer () {}; + auto pub = curve->MulP(priv); + 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); + } - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature); - } + typedef GOSTR3410Verifier GOSTR3410_256_Verifier; + typedef GOSTR3410Signer GOSTR3410_256_Signer; + typedef GOSTR3410Verifier GOSTR3410_512_Verifier; + typedef GOSTR3410Signer 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]; - uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; - }; + RedDSA25519Signer(const uint8_t *signingPrivateKey) { + 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) - { - GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey); - RedDSA25519Signer signer (signingPrivateKey); - memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); - } -} + ~RedDSA25519Signer() {}; + + void Sign(const uint8_t *buf, int len, uint8_t *signature) const { + GetEd25519()->SignRedDSA(m_PrivateKey, m_PublicKeyEncoded, buf, len, signature); + } + + const uint8_t *GetPublicKey() const { return m_PublicKeyEncoded; }; // for keys creation + + private: + + uint8_t m_PrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH]; + uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; + }; + + inline void CreateRedDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) { + GetEd25519()->CreateRedDSAPrivateKey(signingPrivateKey); + RedDSA25519Signer signer(signingPrivateKey); + memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH); + } + } } #endif diff --git a/libi2pd/Siphash.h b/libi2pd/Siphash.h index 78b22b90..0a8c1e1a 100644 --- a/libi2pd/Siphash.h +++ b/libi2pd/Siphash.h @@ -12,143 +12,131 @@ #include "Crypto.h" #if !OPENSSL_SIPHASH -namespace i2p -{ -namespace crypto -{ - namespace siphash - { - constexpr int crounds = 2; - constexpr int drounds = 4; +namespace i2p { + namespace crypto { + namespace siphash { + constexpr int crounds = 2; + constexpr int drounds = 4; - inline uint64_t rotl(const uint64_t & x, int b) - { - uint64_t ret = x << b; - ret |= x >> (64 - b); - return ret; - } + inline uint64_t rotl(const uint64_t &x, int b) { + uint64_t ret = x << b; + ret |= x >> (64 - b); + return ret; + } - inline void u32to8le(const uint32_t & v, uint8_t * p) - { - p[0] = (uint8_t) v; - p[1] = (uint8_t) (v >> 8); - p[2] = (uint8_t) (v >> 16); - p[3] = (uint8_t) (v >> 24); - } + inline void u32to8le(const uint32_t &v, uint8_t *p) { + p[0] = (uint8_t) v; + p[1] = (uint8_t)(v >> 8); + p[2] = (uint8_t)(v >> 16); + p[3] = (uint8_t)(v >> 24); + } - inline void u64to8le(const uint64_t & v, uint8_t * p) - { - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; - p[2] = (v >> 16) & 0xff; - p[3] = (v >> 24) & 0xff; - p[4] = (v >> 32) & 0xff; - p[5] = (v >> 40) & 0xff; - p[6] = (v >> 48) & 0xff; - p[7] = (v >> 56) & 0xff; - } + inline void u64to8le(const uint64_t &v, uint8_t *p) { + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; + p[4] = (v >> 32) & 0xff; + p[5] = (v >> 40) & 0xff; + p[6] = (v >> 48) & 0xff; + p[7] = (v >> 56) & 0xff; + } - inline uint64_t u8to64le(const uint8_t * p) - { - uint64_t i = 0; - int idx = 0; - while(idx < 8) - { - i |= ((uint64_t) p[idx]) << (idx * 8); - ++idx; - } - return i; - } + inline uint64_t u8to64le(const uint8_t *p) { + uint64_t i = 0; + int idx = 0; + while (idx < 8) { + i |= ((uint64_t) p[idx]) << (idx * 8); + ++idx; + } + return i; + } - inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) - { - _v0 += _v1; - _v1 = rotl(_v1, 13); - _v1 ^= _v0; - _v0 = rotl(_v0, 32); - _v2 += _v3; - _v3 = rotl(_v3, 16); - _v3 ^= _v2; - _v0 += _v3; - _v3 = rotl(_v3, 21); - _v3 ^= _v0; - _v2 += _v1; - _v1 = rotl(_v1, 17); - _v1 ^= _v2; - _v2 = rotl(_v2, 32); - } - } + inline void round(uint64_t &_v0, uint64_t &_v1, uint64_t &_v2, uint64_t &_v3) { + _v0 += _v1; + _v1 = rotl(_v1, 13); + _v1 ^= _v0; + _v0 = rotl(_v0, 32); + _v2 += _v3; + _v3 = rotl(_v3, 16); + _v3 ^= _v2; + _v0 += _v3; + _v3 = rotl(_v3, 21); + _v3 ^= _v0; + _v2 += _v1; + _v1 = rotl(_v1, 17); + _v1 ^= _v2; + _v2 = rotl(_v2, 32); + } + } - /** hashsz must be 8 or 16 */ - template - inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) - { - uint64_t v0 = 0x736f6d6570736575ULL; - uint64_t v1 = 0x646f72616e646f6dULL; - uint64_t v2 = 0x6c7967656e657261ULL; - uint64_t v3 = 0x7465646279746573ULL; - const uint64_t k0 = siphash::u8to64le(key); - const uint64_t k1 = siphash::u8to64le(key + 8); - uint64_t msg; - int i; - const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); - auto left = bufsz & 7; - uint64_t b = ((uint64_t)bufsz) << 56; - v3 ^= k1; - v2 ^= k0; - v1 ^= k1; - v0 ^= k0; + /** hashsz must be 8 or 16 */ + template + inline void Siphash(uint8_t *h, const uint8_t *buf, std::size_t bufsz, const uint8_t *key) { + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + const uint64_t k0 = siphash::u8to64le(key); + const uint64_t k1 = siphash::u8to64le(key + 8); + uint64_t msg; + int i; + const uint8_t *end = buf + bufsz - (bufsz % sizeof(uint64_t)); + auto left = bufsz & 7; + uint64_t b = ((uint64_t) bufsz) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; - if(hashsz == 16) v1 ^= 0xee; + if (hashsz == 16) v1 ^= 0xee; - while(buf != end) - { - msg = siphash::u8to64le(buf); - v3 ^= msg; - for(i = 0; i < siphash::crounds; ++i) - siphash::round(v0, v1, v2, v3); + while (buf != end) { + msg = siphash::u8to64le(buf); + v3 ^= msg; + for (i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); - v0 ^= msg; - buf += 8; - } + v0 ^= msg; + buf += 8; + } - while(left) - { - --left; - b |= ((uint64_t)(buf[left])) << (left * 8); - } + while (left) { + --left; + b |= ((uint64_t)(buf[left])) << (left * 8); + } - v3 ^= b; + v3 ^= b; - for(i = 0; i < siphash::crounds; ++i) - siphash::round(v0, v1, v2, v3); + for (i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); - v0 ^= b; + v0 ^= b; - if(hashsz == 16) - v2 ^= 0xee; - else - v2 ^= 0xff; + if (hashsz == 16) + v2 ^= 0xee; + else + v2 ^= 0xff; - for(i = 0; i < siphash::drounds; ++i) - siphash::round(v0, v1, v2, v3); + for (i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); - b = v0 ^ v1 ^ v2 ^ v3; + b = v0 ^ v1 ^ v2 ^ v3; - siphash::u64to8le(b, h); + siphash::u64to8le(b, h); - if(hashsz == 8) return; + if (hashsz == 8) return; - v1 ^= 0xdd; + v1 ^= 0xdd; - for (i = 0; i < siphash::drounds; ++i) - siphash::round(v0, v1, v2, v3); + for (i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); - b = v0 ^ v1 ^ v2 ^ v3; - siphash::u64to8le(b, h + 8); - } -} + b = v0 ^ v1 ^ v2 ^ v3; + siphash::u64to8le(b, h + 8); + } + } } #endif diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 72562675..bbd83b40 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -15,1382 +15,1232 @@ #include "Destination.h" #include "Streaming.h" -namespace i2p -{ -namespace stream -{ - void SendBufferQueue::Add (const uint8_t * buf, size_t len, SendHandler handler) - { - Add (std::make_shared(buf, len, handler)); - } +namespace i2p { + namespace stream { + void SendBufferQueue::Add(const uint8_t *buf, size_t len, SendHandler handler) { + Add(std::make_shared(buf, len, handler)); + } - void SendBufferQueue::Add (std::shared_ptr buf) - { - if (buf) - { - m_Buffers.push_back (buf); - m_Size += buf->len; - } - } + void SendBufferQueue::Add(std::shared_ptr buf) { + if (buf) { + m_Buffers.push_back(buf); + m_Size += buf->len; + } + } - size_t SendBufferQueue::Get (uint8_t * buf, size_t len) - { - size_t offset = 0; - while (!m_Buffers.empty () && offset < len) - { - auto nextBuffer = m_Buffers.front (); - auto rem = nextBuffer->GetRemainingSize (); - if (offset + rem <= len) - { - // whole buffer - memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); - offset += rem; - m_Buffers.pop_front (); // delete it - } - else - { - // partially - rem = len - offset; - memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), len - offset); - nextBuffer->offset += (len - offset); - offset = len; // break - } - } - m_Size -= offset; - return offset; - } + size_t SendBufferQueue::Get(uint8_t *buf, size_t len) { + size_t offset = 0; + while (!m_Buffers.empty() && offset < len) { + auto nextBuffer = m_Buffers.front(); + auto rem = nextBuffer->GetRemainingSize(); + if (offset + rem <= len) { + // whole buffer + memcpy(buf + offset, nextBuffer->GetRemaningBuffer(), rem); + offset += rem; + m_Buffers.pop_front(); // delete it + } else { + // partially + rem = len - offset; + memcpy(buf + offset, nextBuffer->GetRemaningBuffer(), len - offset); + nextBuffer->offset += (len - offset); + offset = len; // break + } + } + m_Size -= offset; + return offset; + } - void SendBufferQueue::CleanUp () - { - if (!m_Buffers.empty ()) - { - for (auto it: m_Buffers) - it->Cancel (); - m_Buffers.clear (); - m_Size = 0; - } - } + void SendBufferQueue::CleanUp() { + if (!m_Buffers.empty()) { + for (auto it: m_Buffers) + it->Cancel(); + m_Buffers.clear(); + m_Size = 0; + } + } - Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, - std::shared_ptr remote, int port): m_Service (service), - m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), - m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), - m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), - m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), - m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), - m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU) - { - RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); - m_RemoteIdentity = remote->GetIdentity (); - } + Stream::Stream(boost::asio::io_service &service, StreamingDestination &local, + std::shared_ptr remote, int port) : m_Service(service), + m_SendStreamID(0), + m_SequenceNumber(0), + m_LastReceivedSequenceNumber(-1), + m_Status(eStreamStatusNew), + m_IsAckSendScheduled(false), + m_LocalDestination(local), + m_RemoteLeaseSet(remote), + m_ReceiveTimer(m_Service), + m_ResendTimer(m_Service), + m_AckSendTimer(m_Service), + m_NumSentBytes(0), + m_NumReceivedBytes(0), + m_Port(port), + m_WindowSize(MIN_WINDOW_SIZE), + m_RTT(INITIAL_RTT), + m_RTO(INITIAL_RTO), + m_AckDelay( + local.GetOwner()->GetStreamingAckDelay()), + m_LastWindowSizeIncreaseTime(0), + m_NumResendAttempts(0), + m_MTU(STREAMING_MTU) { + RAND_bytes((uint8_t * ) & m_RecvStreamID, 4); + m_RemoteIdentity = remote->GetIdentity(); + } - Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): - m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), - m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), - m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), - m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), - m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU) - { - RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); - } + Stream::Stream(boost::asio::io_service &service, StreamingDestination &local) : + m_Service(service), m_SendStreamID(0), m_SequenceNumber(0), m_LastReceivedSequenceNumber(-1), + m_Status(eStreamStatusNew), m_IsAckSendScheduled(false), m_LocalDestination(local), + m_ReceiveTimer(m_Service), m_ResendTimer(m_Service), m_AckSendTimer(m_Service), + m_NumSentBytes(0), m_NumReceivedBytes(0), m_Port(0), m_WindowSize(MIN_WINDOW_SIZE), + m_RTT(INITIAL_RTT), m_RTO(INITIAL_RTO), m_AckDelay(local.GetOwner()->GetStreamingAckDelay()), + m_LastWindowSizeIncreaseTime(0), m_NumResendAttempts(0), m_MTU(STREAMING_MTU) { + RAND_bytes((uint8_t * ) & m_RecvStreamID, 4); + } - Stream::~Stream () - { - CleanUp (); - LogPrint (eLogDebug, "Streaming: Stream deleted"); - } + Stream::~Stream() { + CleanUp(); + LogPrint(eLogDebug, "Streaming: Stream deleted"); + } - void Stream::Terminate (bool deleteFromDestination) // should be called from StreamingDestination::Stop only - { - m_Status = eStreamStatusTerminated; - m_AckSendTimer.cancel (); - m_ReceiveTimer.cancel (); - m_ResendTimer.cancel (); - //CleanUp (); /* Need to recheck - broke working on windows */ - if (deleteFromDestination) - m_LocalDestination.DeleteStream (shared_from_this ()); - } + void Stream::Terminate(bool deleteFromDestination) // should be called from StreamingDestination::Stop only + { + m_Status = eStreamStatusTerminated; + m_AckSendTimer.cancel(); + m_ReceiveTimer.cancel(); + m_ResendTimer.cancel(); + //CleanUp (); /* Need to recheck - broke working on windows */ + if (deleteFromDestination) + m_LocalDestination.DeleteStream(shared_from_this()); + } - void Stream::CleanUp () - { - { - std::unique_lock l(m_SendBufferMutex); - m_SendBuffer.CleanUp (); - } - while (!m_ReceiveQueue.empty ()) - { - auto packet = m_ReceiveQueue.front (); - m_ReceiveQueue.pop (); - m_LocalDestination.DeletePacket (packet); - } + void Stream::CleanUp() { + { + std::unique_lock l(m_SendBufferMutex); + m_SendBuffer.CleanUp(); + } + while (!m_ReceiveQueue.empty()) { + auto packet = m_ReceiveQueue.front(); + m_ReceiveQueue.pop(); + m_LocalDestination.DeletePacket(packet); + } - for (auto it: m_SentPackets) - m_LocalDestination.DeletePacket (it); - m_SentPackets.clear (); + for (auto it: m_SentPackets) + m_LocalDestination.DeletePacket(it); + m_SentPackets.clear(); - for (auto it: m_SavedPackets) - m_LocalDestination.DeletePacket (it); - m_SavedPackets.clear (); - } + for (auto it: m_SavedPackets) + m_LocalDestination.DeletePacket(it); + m_SavedPackets.clear(); + } - void Stream::HandleNextPacket (Packet * packet) - { - m_NumReceivedBytes += packet->GetLength (); - if (!m_SendStreamID) - m_SendStreamID = packet->GetReceiveStreamID (); + void Stream::HandleNextPacket(Packet *packet) { + m_NumReceivedBytes += packet->GetLength(); + if (!m_SendStreamID) + m_SendStreamID = packet->GetReceiveStreamID(); - if (!packet->IsNoAck ()) // ack received - ProcessAck (packet); + if (!packet->IsNoAck()) // ack received + ProcessAck(packet); - int32_t receivedSeqn = packet->GetSeqn (); - bool isSyn = packet->IsSYN (); - if (!receivedSeqn && !isSyn) - { - // plain ack - LogPrint (eLogDebug, "Streaming: Plain ACK received"); - m_LocalDestination.DeletePacket (packet); - return; - } + int32_t receivedSeqn = packet->GetSeqn(); + bool isSyn = packet->IsSYN(); + if (!receivedSeqn && !isSyn) { + // plain ack + LogPrint(eLogDebug, "Streaming: Plain ACK received"); + m_LocalDestination.DeletePacket(packet); + return; + } - LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID); - if (receivedSeqn == m_LastReceivedSequenceNumber + 1) - { - // we have received next in sequence message - ProcessPacket (packet); + LogPrint(eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID); + if (receivedSeqn == m_LastReceivedSequenceNumber + 1) { + // we have received next in sequence message + ProcessPacket(packet); - // we should also try stored messages if any - for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) - { - if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) - { - Packet * savedPacket = *it; - m_SavedPackets.erase (it++); + // we should also try stored messages if any + for (auto it = m_SavedPackets.begin(); it != m_SavedPackets.end();) { + if ((*it)->GetSeqn() == (uint32_t)(m_LastReceivedSequenceNumber + 1)) { + Packet *savedPacket = *it; + m_SavedPackets.erase(it++); - ProcessPacket (savedPacket); - } - else - break; - } + ProcessPacket(savedPacket); + } else + break; + } - // schedule ack for last message - if (m_Status == eStreamStatusOpen) - { - if (!m_IsAckSendScheduled) - { - m_IsAckSendScheduled = true; - auto ackTimeout = m_RTT/10; - if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - else if (ackTimeout < MIN_SEND_ACK_TIMEOUT) ackTimeout = MIN_SEND_ACK_TIMEOUT; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ackTimeout)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - else if (isSyn) - // we have to send SYN back to incoming connection - SendBuffer (); // also sets m_IsOpen - } - else - { - if (receivedSeqn <= m_LastReceivedSequenceNumber) - { - // we have received duplicate - LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); - SendQuickAck (); // resend ack for previous message again - m_LocalDestination.DeletePacket (packet); // packet dropped - } - else - { - LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); - // save message and wait for missing message again - SavePacket (packet); - if (m_LastReceivedSequenceNumber >= 0) - { - // send NACKs for missing messages ASAP - if (m_IsAckSendScheduled) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - SendQuickAck (); - } - else - { - // wait for SYN - m_IsAckSendScheduled = true; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(SYN_TIMEOUT)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - } - } + // schedule ack for last message + if (m_Status == eStreamStatusOpen) { + if (!m_IsAckSendScheduled) { + m_IsAckSendScheduled = true; + auto ackTimeout = m_RTT / 10; + if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; + else if (ackTimeout < MIN_SEND_ACK_TIMEOUT) ackTimeout = MIN_SEND_ACK_TIMEOUT; + m_AckSendTimer.expires_from_now(boost::posix_time::milliseconds(ackTimeout)); + m_AckSendTimer.async_wait(std::bind(&Stream::HandleAckSendTimer, + shared_from_this(), std::placeholders::_1)); + } + } else if (isSyn) + // we have to send SYN back to incoming connection + SendBuffer(); // also sets m_IsOpen + } else { + if (receivedSeqn <= m_LastReceivedSequenceNumber) { + // we have received duplicate + LogPrint(eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); + SendQuickAck(); // resend ack for previous message again + m_LocalDestination.DeletePacket(packet); // packet dropped + } else { + LogPrint(eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", + m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); + // save message and wait for missing message again + SavePacket(packet); + if (m_LastReceivedSequenceNumber >= 0) { + // send NACKs for missing messages ASAP + if (m_IsAckSendScheduled) { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel(); + } + SendQuickAck(); + } else { + // wait for SYN + m_IsAckSendScheduled = true; + m_AckSendTimer.expires_from_now(boost::posix_time::milliseconds(SYN_TIMEOUT)); + m_AckSendTimer.async_wait(std::bind(&Stream::HandleAckSendTimer, + shared_from_this(), std::placeholders::_1)); + } + } + } + } - void Stream::SavePacket (Packet * packet) - { - if (!m_SavedPackets.insert (packet).second) - m_LocalDestination.DeletePacket (packet); - } + void Stream::SavePacket(Packet *packet) { + if (!m_SavedPackets.insert(packet).second) + m_LocalDestination.DeletePacket(packet); + } - void Stream::ProcessPacket (Packet * packet) - { - uint32_t receivedSeqn = packet->GetSeqn (); - uint16_t flags = packet->GetFlags (); - LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags); + void Stream::ProcessPacket(Packet *packet) { + uint32_t receivedSeqn = packet->GetSeqn(); + uint16_t flags = packet->GetFlags(); + LogPrint(eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags); - if (!ProcessOptions (flags, packet)) - { - m_LocalDestination.DeletePacket (packet); - Terminate (); - return; - } + if (!ProcessOptions(flags, packet)) { + m_LocalDestination.DeletePacket(packet); + Terminate(); + return; + } - packet->offset = packet->GetPayload () - packet->buf; - if (packet->GetLength () > 0) - { - m_ReceiveQueue.push (packet); - m_ReceiveTimer.cancel (); - } - else - m_LocalDestination.DeletePacket (packet); + packet->offset = packet->GetPayload() - packet->buf; + if (packet->GetLength() > 0) { + m_ReceiveQueue.push(packet); + m_ReceiveTimer.cancel(); + } else + m_LocalDestination.DeletePacket(packet); - m_LastReceivedSequenceNumber = receivedSeqn; + m_LastReceivedSequenceNumber = receivedSeqn; - if (flags & PACKET_FLAG_RESET) - { - LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn); - m_Status = eStreamStatusReset; - Close (); - } - else if (flags & PACKET_FLAG_CLOSE) - { - if (m_Status != eStreamStatusClosed) - SendClose (); - m_Status = eStreamStatusClosed; - Terminate (); - } - } + if (flags & PACKET_FLAG_RESET) { + LogPrint(eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, + ": reset flag received in packet #", receivedSeqn); + m_Status = eStreamStatusReset; + Close(); + } else if (flags & PACKET_FLAG_CLOSE) { + if (m_Status != eStreamStatusClosed) + SendClose(); + m_Status = eStreamStatusClosed; + Terminate(); + } + } - bool Stream::ProcessOptions (uint16_t flags, Packet * packet) - { - const uint8_t * optionData = packet->GetOptionData (); - size_t optionSize = packet->GetOptionSize (); - if (flags & PACKET_FLAG_DELAY_REQUESTED) - { - if (!m_IsAckSendScheduled) - { - uint16_t delayRequested = bufbe16toh (optionData); - if (delayRequested > 0 && delayRequested < m_RTT) - { - m_IsAckSendScheduled = true; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - optionData += 2; - } + bool Stream::ProcessOptions(uint16_t flags, Packet *packet) { + const uint8_t *optionData = packet->GetOptionData(); + size_t optionSize = packet->GetOptionSize(); + if (flags & PACKET_FLAG_DELAY_REQUESTED) { + if (!m_IsAckSendScheduled) { + uint16_t delayRequested = bufbe16toh(optionData); + if (delayRequested > 0 && delayRequested < m_RTT) { + m_IsAckSendScheduled = true; + m_AckSendTimer.expires_from_now(boost::posix_time::milliseconds(delayRequested)); + m_AckSendTimer.async_wait(std::bind(&Stream::HandleAckSendTimer, + shared_from_this(), std::placeholders::_1)); + } + } + optionData += 2; + } - if (flags & PACKET_FLAG_FROM_INCLUDED) - { - if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); - if (!m_RemoteIdentity) - m_RemoteIdentity = std::make_shared(optionData, optionSize); - if (m_RemoteIdentity->IsRSA ()) - { - LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded"); - return false; - } - optionData += m_RemoteIdentity->GetFullLen (); - if (!m_RemoteLeaseSet) - LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - } + if (flags & PACKET_FLAG_FROM_INCLUDED) { + if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity(); + if (!m_RemoteIdentity) + m_RemoteIdentity = std::make_shared(optionData, optionSize); + if (m_RemoteIdentity->IsRSA()) { + LogPrint(eLogInfo, "Streaming: Incoming stream from RSA destination ", + m_RemoteIdentity->GetIdentHash().ToBase64(), " Discarded"); + return false; + } + optionData += m_RemoteIdentity->GetFullLen(); + if (!m_RemoteLeaseSet) + LogPrint(eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash().ToBase64(), + ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); + } - if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) - { - uint16_t maxPacketSize = bufbe16toh (optionData); - LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize); - optionData += 2; - } + if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) { + uint16_t maxPacketSize = bufbe16toh(optionData); + LogPrint(eLogDebug, "Streaming: Max packet size ", maxPacketSize); + optionData += 2; + } - if (flags & PACKET_FLAG_OFFLINE_SIGNATURE) - { - if (!m_RemoteIdentity) - { - LogPrint (eLogInfo, "Streaming: offline signature without identity"); - return false; - } - // if we have it in LeaseSet already we don't need to parse it again - if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); - if (m_TransientVerifier) - { - // skip option data - optionData += 6; // timestamp and key type - optionData += m_TransientVerifier->GetPublicKeyLen (); // public key - optionData += m_RemoteIdentity->GetSignatureLen (); // signature - } - else - { - // transient key - size_t offset = 0; - m_TransientVerifier = i2p::data::ProcessOfflineSignature (m_RemoteIdentity, optionData, optionSize - (optionData - packet->GetOptionData ()), offset); - optionData += offset; - if (!m_TransientVerifier) - { - LogPrint (eLogError, "Streaming: offline signature failed"); - return false; - } - } - } + if (flags & PACKET_FLAG_OFFLINE_SIGNATURE) { + if (!m_RemoteIdentity) { + LogPrint(eLogInfo, "Streaming: offline signature without identity"); + return false; + } + // if we have it in LeaseSet already we don't need to parse it again + if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier(); + if (m_TransientVerifier) { + // skip option data + optionData += 6; // timestamp and key type + optionData += m_TransientVerifier->GetPublicKeyLen(); // public key + optionData += m_RemoteIdentity->GetSignatureLen(); // signature + } else { + // transient key + size_t offset = 0; + m_TransientVerifier = i2p::data::ProcessOfflineSignature(m_RemoteIdentity, optionData, optionSize - + (optionData - + packet->GetOptionData()), + offset); + optionData += offset; + if (!m_TransientVerifier) { + LogPrint(eLogError, "Streaming: offline signature failed"); + return false; + } + } + } - if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) - { - uint8_t signature[256]; - auto signatureLen = m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : m_RemoteIdentity->GetSignatureLen (); - if(signatureLen <= sizeof(signature)) - { - memcpy (signature, optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - bool verified = m_TransientVerifier ? - m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) : - m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature); - if (!verified) - { - LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - Close (); - flags |= PACKET_FLAG_CLOSE; - } - memcpy (const_cast(optionData), signature, signatureLen); - optionData += signatureLen; - } - else - { - LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); - return false; - } - } - return true; - } + if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) { + uint8_t signature[256]; + auto signatureLen = m_TransientVerifier ? m_TransientVerifier->GetSignatureLen() + : m_RemoteIdentity->GetSignatureLen(); + if (signatureLen <= sizeof(signature)) { + memcpy(signature, optionData, signatureLen); + memset(const_cast(optionData), 0, signatureLen); + bool verified = m_TransientVerifier ? + m_TransientVerifier->Verify(packet->GetBuffer(), packet->GetLength(), signature) : + m_RemoteIdentity->Verify(packet->GetBuffer(), packet->GetLength(), signature); + if (!verified) { + LogPrint(eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, + ", rSID=", m_RecvStreamID); + Close(); + flags |= PACKET_FLAG_CLOSE; + } + memcpy(const_cast(optionData), signature, signatureLen); + optionData += signatureLen; + } else { + LogPrint(eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); + return false; + } + } + return true; + } - void Stream::HandlePing (Packet * packet) - { - uint16_t flags = packet->GetFlags (); - if (ProcessOptions (flags, packet) && m_RemoteIdentity) - { - // send pong - Packet p; - memset (p.buf, 0, 22); // minimal header all zeroes - memcpy (p.buf + 4, packet->buf, 4); // but receiveStreamID is the sendStreamID from the ping - htobe16buf (p.buf + 18, PACKET_FLAG_ECHO); // and echo flag - ssize_t payloadLen = packet->len - (packet->GetPayload () - packet->buf); - if (payloadLen > 0) - memcpy (p.buf + 22, packet->GetPayload (), payloadLen); - else - payloadLen = 0; - p.len = payloadLen + 22; - SendPackets (std::vector { &p }); - LogPrint (eLogDebug, "Streaming: Pong of ", p.len, " bytes sent"); - } - m_LocalDestination.DeletePacket (packet); - } + void Stream::HandlePing(Packet *packet) { + uint16_t flags = packet->GetFlags(); + if (ProcessOptions(flags, packet) && m_RemoteIdentity) { + // send pong + Packet p; + memset(p.buf, 0, 22); // minimal header all zeroes + memcpy(p.buf + 4, packet->buf, 4); // but receiveStreamID is the sendStreamID from the ping + htobe16buf(p.buf + 18, PACKET_FLAG_ECHO); // and echo flag + ssize_t payloadLen = packet->len - (packet->GetPayload() - packet->buf); + if (payloadLen > 0) + memcpy(p.buf + 22, packet->GetPayload(), payloadLen); + else + payloadLen = 0; + p.len = payloadLen + 22; + SendPackets(std::vector{&p}); + LogPrint(eLogDebug, "Streaming: Pong of ", p.len, " bytes sent"); + } + m_LocalDestination.DeletePacket(packet); + } - void Stream::ProcessAck (Packet * packet) - { - bool acknowledged = false; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t ackThrough = packet->GetAckThrough (); - if (ackThrough > m_SequenceNumber) - { - LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); - return; - } - int nackCount = packet->GetNACKCount (); - for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) - { - auto seqn = (*it)->GetSeqn (); - if (seqn <= ackThrough) - { - if (nackCount > 0) - { - bool nacked = false; - for (int i = 0; i < nackCount; i++) - if (seqn == packet->GetNACK (i)) - { - nacked = true; - break; - } - if (nacked) - { - LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK"); - ++it; - continue; - } - } - auto sentPacket = *it; - uint64_t rtt = ts - sentPacket->sendTime; - if(ts < sentPacket->sendTime) - { - LogPrint(eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime); - rtt = 1; - } - m_RTT = (m_RTT*seqn + rtt)/(seqn + 1); - m_RTO = m_RTT*1.5; // TODO: implement it better - LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); - m_SentPackets.erase (it++); - m_LocalDestination.DeletePacket (sentPacket); - acknowledged = true; - if (m_WindowSize < WINDOW_SIZE) - m_WindowSize++; // slow start - else - { - // linear growth - if (ts > m_LastWindowSizeIncreaseTime + m_RTT) - { - m_WindowSize++; - if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; - m_LastWindowSizeIncreaseTime = ts; - } - } - if (!seqn && m_RoutingSession) // first message confirmed - m_RoutingSession->SetSharedRoutingPath ( - std::make_shared ( - i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, m_RTT, 0, 0})); - } - else - break; - } - if (m_SentPackets.empty ()) - m_ResendTimer.cancel (); - if (acknowledged) - { - m_NumResendAttempts = 0; - SendBuffer (); - } - if (m_Status == eStreamStatusClosed) - Terminate (); - else if (m_Status == eStreamStatusClosing) - Close (); // check is all outgoing messages have been sent and we can send close - } + void Stream::ProcessAck(Packet *packet) { + bool acknowledged = false; + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + uint32_t ackThrough = packet->GetAckThrough(); + if (ackThrough > m_SequenceNumber) { + LogPrint(eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); + return; + } + int nackCount = packet->GetNACKCount(); + for (auto it = m_SentPackets.begin(); it != m_SentPackets.end();) { + auto seqn = (*it)->GetSeqn(); + if (seqn <= ackThrough) { + if (nackCount > 0) { + bool nacked = false; + for (int i = 0; i < nackCount; i++) + if (seqn == packet->GetNACK(i)) { + nacked = true; + break; + } + if (nacked) { + LogPrint(eLogDebug, "Streaming: Packet ", seqn, " NACK"); + ++it; + continue; + } + } + auto sentPacket = *it; + uint64_t rtt = ts - sentPacket->sendTime; + if (ts < sentPacket->sendTime) { + LogPrint(eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", + sentPacket->sendTime); + rtt = 1; + } + m_RTT = (m_RTT * seqn + rtt) / (seqn + 1); + m_RTO = m_RTT * 1.5; // TODO: implement it better + LogPrint(eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", + sentPacket->sendTime); + m_SentPackets.erase(it++); + m_LocalDestination.DeletePacket(sentPacket); + acknowledged = true; + if (m_WindowSize < WINDOW_SIZE) + m_WindowSize++; // slow start + else { + // linear growth + if (ts > m_LastWindowSizeIncreaseTime + m_RTT) { + m_WindowSize++; + if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; + m_LastWindowSizeIncreaseTime = ts; + } + } + if (!seqn && m_RoutingSession) // first message confirmed + m_RoutingSession->SetSharedRoutingPath( + std::make_shared( + i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, + m_RTT, 0, 0})); + } else + break; + } + if (m_SentPackets.empty()) + m_ResendTimer.cancel(); + if (acknowledged) { + m_NumResendAttempts = 0; + SendBuffer(); + } + if (m_Status == eStreamStatusClosed) + Terminate(); + else if (m_Status == eStreamStatusClosing) + Close(); // check is all outgoing messages have been sent and we can send close + } - size_t Stream::Send (const uint8_t * buf, size_t len) - { - AsyncSend (buf, len, nullptr); - return len; - } + size_t Stream::Send(const uint8_t *buf, size_t len) { + AsyncSend(buf, len, nullptr); + return len; + } - void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) - { - if (len > 0 && buf) - { - std::unique_lock l(m_SendBufferMutex); - m_SendBuffer.Add (buf, len, handler); - } - else if (handler) - handler(boost::system::error_code ()); - m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); - } + void Stream::AsyncSend(const uint8_t *buf, size_t len, SendHandler handler) { + if (len > 0 && buf) { + std::unique_lock l(m_SendBufferMutex); + m_SendBuffer.Add(buf, len, handler); + } else if (handler) + handler(boost::system::error_code()); + m_Service.post(std::bind(&Stream::SendBuffer, shared_from_this())); + } - void Stream::SendBuffer () - { - int numMsgs = m_WindowSize - m_SentPackets.size (); - if (numMsgs <= 0) return; // window is full + void Stream::SendBuffer() { + int numMsgs = m_WindowSize - m_SentPackets.size(); + if (numMsgs <= 0) return; // window is full - bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet - std::vector packets; - { - std::unique_lock l(m_SendBufferMutex); - while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0)) - { - Packet * p = m_LocalDestination.NewPacket (); - uint8_t * packet = p->GetBuffer (); - // TODO: implement setters - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - if (isNoAck) - htobuf32 (packet + size, 0); - else - htobe32buf (packet + size, m_LastReceivedSequenceNumber); - size += 4; // ack Through - packet[size] = 0; - size++; // NACK count - packet[size] = m_RTO/1000; - size++; // resend delay - if (m_Status == eStreamStatusNew) - { - // initial packet - m_Status = eStreamStatusOpen; - if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());; - if (m_RemoteLeaseSet) - { - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); - m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU; - } - uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | - PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; - if (isNoAck) flags |= PACKET_FLAG_NO_ACK; - bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); - if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; - htobe16buf (packet + size, flags); - size += 2; // flags - size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - uint8_t * optionsSize = packet + size; // set options size later - size += 2; // options size - m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); - size += identityLen; // from - htobe16buf (packet + size, m_MTU); - size += 2; // max packet size - if (isOfflineSignature) - { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature - } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - size += m_SendBuffer.Get (packet + size, m_MTU); // payload - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - } - else - { - // follow on packet - htobuf16 (packet + size, 0); - size += 2; // flags - htobuf16 (packet + size, 0); // no options - size += 2; // options size - size += m_SendBuffer.Get(packet + size, m_MTU); // payload - } - p->len = size; - packets.push_back (p); - numMsgs--; - } - } - if (packets.size () > 0) - { - if (m_SavedPackets.empty ()) // no NACKS - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - bool isEmpty = m_SentPackets.empty (); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto& it: packets) - { - it->sendTime = ts; - m_SentPackets.insert (it); - } - SendPackets (packets); - if (m_Status == eStreamStatusClosing && m_SendBuffer.IsEmpty ()) - SendClose (); - if (isEmpty) - ScheduleResend (); - } - } + bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet + std::vector packets; + { + std::unique_lock l(m_SendBufferMutex); + while ((m_Status == eStreamStatusNew) || (IsEstablished() && !m_SendBuffer.IsEmpty() && numMsgs > 0)) { + Packet *p = m_LocalDestination.NewPacket(); + uint8_t *packet = p->GetBuffer(); + // TODO: implement setters + size_t size = 0; + htobe32buf(packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf(packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf(packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + if (isNoAck) + htobuf32(packet + size, 0); + else + htobe32buf(packet + size, m_LastReceivedSequenceNumber); + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + packet[size] = m_RTO / 1000; + size++; // resend delay + if (m_Status == eStreamStatusNew) { + // initial packet + m_Status = eStreamStatusOpen; + if (!m_RemoteLeaseSet) + m_RemoteLeaseSet = m_LocalDestination.GetOwner()->FindLeaseSet( + m_RemoteIdentity->GetIdentHash());; + if (m_RemoteLeaseSet) { + m_RoutingSession = m_LocalDestination.GetOwner()->GetRoutingSession(m_RemoteLeaseSet, true); + m_MTU = m_RoutingSession->IsRatchets() ? STREAMING_MTU_RATCHETS : STREAMING_MTU; + } + uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | + PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; + if (isNoAck) flags |= PACKET_FLAG_NO_ACK; + bool isOfflineSignature = m_LocalDestination.GetOwner()->GetPrivateKeys().IsOfflineSignature(); + if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; + htobe16buf(packet + size, flags); + size += 2; // flags + size_t identityLen = m_LocalDestination.GetOwner()->GetIdentity()->GetFullLen(); + size_t signatureLen = m_LocalDestination.GetOwner()->GetPrivateKeys().GetSignatureLen(); + uint8_t *optionsSize = packet + size; // set options size later + size += 2; // options size + m_LocalDestination.GetOwner()->GetIdentity()->ToBuffer(packet + size, identityLen); + size += identityLen; // from + htobe16buf(packet + size, m_MTU); + size += 2; // max packet size + if (isOfflineSignature) { + const auto &offlineSignature = m_LocalDestination.GetOwner()->GetPrivateKeys().GetOfflineSignature(); + memcpy(packet + size, offlineSignature.data(), offlineSignature.size()); + size += offlineSignature.size(); // offline signature + } + uint8_t *signature = packet + size; // set it later + memset(signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + htobe16buf(optionsSize, packet + size - 2 - optionsSize); // actual options size + size += m_SendBuffer.Get(packet + size, m_MTU); // payload + m_LocalDestination.GetOwner()->Sign(packet, size, signature); + } else { + // follow on packet + htobuf16(packet + size, 0); + size += 2; // flags + htobuf16(packet + size, 0); // no options + size += 2; // options size + size += m_SendBuffer.Get(packet + size, m_MTU); // payload + } + p->len = size; + packets.push_back(p); + numMsgs--; + } + } + if (packets.size() > 0) { + if (m_SavedPackets.empty()) // no NACKS + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel(); + } + bool isEmpty = m_SentPackets.empty(); + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + for (auto &it: packets) { + it->sendTime = ts; + m_SentPackets.insert(it); + } + SendPackets(packets); + if (m_Status == eStreamStatusClosing && m_SendBuffer.IsEmpty()) + SendClose(); + if (isEmpty) + ScheduleResend(); + } + } - void Stream::SendQuickAck () - { - int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; - if (!m_SavedPackets.empty ()) - { - int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn (); - if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; - } - if (lastReceivedSeqn < 0) - { - LogPrint (eLogError, "Streaming: No packets have been received yet"); - return; - } + void Stream::SendQuickAck() { + int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; + if (!m_SavedPackets.empty()) { + int32_t seqn = (*m_SavedPackets.rbegin())->GetSeqn(); + if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; + } + if (lastReceivedSeqn < 0) { + LogPrint(eLogError, "Streaming: No packets have been received yet"); + return; + } - Packet p; - uint8_t * packet = p.GetBuffer (); - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobuf32 (packet + size, 0); // this is plain Ack message - size += 4; // sequenceNum - htobe32buf (packet + size, lastReceivedSeqn); - size += 4; // ack Through - uint8_t numNacks = 0; - if (lastReceivedSeqn > m_LastReceivedSequenceNumber) - { - // fill NACKs - uint8_t * nacks = packet + size + 1; - auto nextSeqn = m_LastReceivedSequenceNumber + 1; - for (auto it: m_SavedPackets) - { - auto seqn = it->GetSeqn (); - if (numNacks + (seqn - nextSeqn) >= 256) - { - LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); - htobe32buf (packet + 12, nextSeqn); // change ack Through - break; - } - for (uint32_t i = nextSeqn; i < seqn; i++) - { - htobe32buf (nacks, i); - nacks += 4; - numNacks++; - } - nextSeqn = seqn + 1; - } - packet[size] = numNacks; - size++; // NACK count - size += numNacks*4; // NACKs - } - else - { - // No NACKs - packet[size] = 0; - size++; // NACK count - } - packet[size] = 0; - size++; // resend delay - htobuf16 (packet + size, 0); // no flags set - size += 2; // flags - htobuf16 (packet + size, 0); // no options - size += 2; // options size - p.len = size; + Packet p; + uint8_t *packet = p.GetBuffer(); + size_t size = 0; + htobe32buf(packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf(packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobuf32(packet + size, 0); // this is plain Ack message + size += 4; // sequenceNum + htobe32buf(packet + size, lastReceivedSeqn); + size += 4; // ack Through + uint8_t numNacks = 0; + if (lastReceivedSeqn > m_LastReceivedSequenceNumber) { + // fill NACKs + uint8_t *nacks = packet + size + 1; + auto nextSeqn = m_LastReceivedSequenceNumber + 1; + for (auto it: m_SavedPackets) { + auto seqn = it->GetSeqn(); + if (numNacks + (seqn - nextSeqn) >= 256) { + LogPrint(eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", + nextSeqn); + htobe32buf(packet + 12, nextSeqn); // change ack Through + break; + } + for (uint32_t i = nextSeqn; i < seqn; i++) { + htobe32buf(nacks, i); + nacks += 4; + numNacks++; + } + nextSeqn = seqn + 1; + } + packet[size] = numNacks; + size++; // NACK count + size += numNacks * 4; // NACKs + } else { + // No NACKs + packet[size] = 0; + size++; // NACK count + } + packet[size] = 0; + size++; // resend delay + htobuf16(packet + size, 0); // no flags set + size += 2; // flags + htobuf16(packet + size, 0); // no options + size += 2; // options size + p.len = size; - SendPackets (std::vector { &p }); - LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs"); - } + SendPackets(std::vector{&p}); + LogPrint(eLogDebug, "Streaming: Quick Ack sent. ", (int) numNacks, " NACKs"); + } - void Stream::SendPing () - { - Packet p; - uint8_t * packet = p.GetBuffer (); - size_t size = 0; - htobe32buf (packet, m_RecvStreamID); - size += 4; // sendStreamID - memset (packet + size, 0, 14); - size += 14; // all zeroes - uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_FROM_INCLUDED; - bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); - if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; - htobe16buf (packet + size, flags); - size += 2; // flags - size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - uint8_t * optionsSize = packet + size; // set options size later - size += 2; // options size - m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); - size += identityLen; // from - if (isOfflineSignature) - { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature - } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - p.len = size; - SendPackets (std::vector { &p }); - LogPrint (eLogDebug, "Streaming: Ping of ", p.len, " bytes sent"); - } + void Stream::SendPing() { + Packet p; + uint8_t *packet = p.GetBuffer(); + size_t size = 0; + htobe32buf(packet, m_RecvStreamID); + size += 4; // sendStreamID + memset(packet + size, 0, 14); + size += 14; // all zeroes + uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_FROM_INCLUDED; + bool isOfflineSignature = m_LocalDestination.GetOwner()->GetPrivateKeys().IsOfflineSignature(); + if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; + htobe16buf(packet + size, flags); + size += 2; // flags + size_t identityLen = m_LocalDestination.GetOwner()->GetIdentity()->GetFullLen(); + size_t signatureLen = m_LocalDestination.GetOwner()->GetPrivateKeys().GetSignatureLen(); + uint8_t *optionsSize = packet + size; // set options size later + size += 2; // options size + m_LocalDestination.GetOwner()->GetIdentity()->ToBuffer(packet + size, identityLen); + size += identityLen; // from + if (isOfflineSignature) { + const auto &offlineSignature = m_LocalDestination.GetOwner()->GetPrivateKeys().GetOfflineSignature(); + memcpy(packet + size, offlineSignature.data(), offlineSignature.size()); + size += offlineSignature.size(); // offline signature + } + uint8_t *signature = packet + size; // set it later + memset(signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + htobe16buf(optionsSize, packet + size - 2 - optionsSize); // actual options size + m_LocalDestination.GetOwner()->Sign(packet, size, signature); + p.len = size; + SendPackets(std::vector{&p}); + LogPrint(eLogDebug, "Streaming: Ping of ", p.len, " bytes sent"); + } - void Stream::Close () - { - LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status); - switch (m_Status) - { - case eStreamStatusOpen: - m_Status = eStreamStatusClosing; - Close (); // recursion - if (m_Status == eStreamStatusClosing) //still closing - LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID); - break; - case eStreamStatusReset: - // TODO: send reset - Terminate (); - break; - case eStreamStatusClosing: - if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) // nothing to send - { - m_Status = eStreamStatusClosed; - SendClose(); - } - break; - case eStreamStatusClosed: - // already closed - Terminate (); - break; - default: - LogPrint (eLogWarning, "Streaming: Unexpected stream status=", (int)m_Status, " for sSID=", m_SendStreamID); - }; - } + void Stream::Close() { + LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, + ", status=", m_Status); + switch (m_Status) { + case eStreamStatusOpen: + m_Status = eStreamStatusClosing; + Close(); // recursion + if (m_Status == eStreamStatusClosing) //still closing + LogPrint(eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", + m_SendStreamID); + break; + case eStreamStatusReset: + // TODO: send reset + Terminate(); + break; + case eStreamStatusClosing: + if (m_SentPackets.empty() && m_SendBuffer.IsEmpty()) // nothing to send + { + m_Status = eStreamStatusClosed; + SendClose(); + } + break; + case eStreamStatusClosed: + // already closed + Terminate(); + break; + default: + LogPrint(eLogWarning, "Streaming: Unexpected stream status=", (int) m_Status, " for sSID=", + m_SendStreamID); + }; + } - void Stream::SendClose () - { - Packet * p = m_LocalDestination.NewPacket (); - uint8_t * packet = p->GetBuffer (); - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); - size += 4; // ack Through - packet[size] = 0; - size++; // NACK count - packet[size] = 0; - size++; // resend delay - htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); - size += 2; // flags - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - htobe16buf (packet + size, signatureLen); // signature only - size += 2; // options size - uint8_t * signature = packet + size; - memset (packet + size, 0, signatureLen); - size += signatureLen; // signature - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + void Stream::SendClose() { + Packet *p = m_LocalDestination.NewPacket(); + uint8_t *packet = p->GetBuffer(); + size_t size = 0; + htobe32buf(packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf(packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf(packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + htobe32buf(packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + packet[size] = 0; + size++; // resend delay + htobe16buf(packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); + size += 2; // flags + size_t signatureLen = m_LocalDestination.GetOwner()->GetPrivateKeys().GetSignatureLen(); + htobe16buf(packet + size, signatureLen); // signature only + size += 2; // options size + uint8_t *signature = packet + size; + memset(packet + size, 0, signatureLen); + size += signatureLen; // signature + m_LocalDestination.GetOwner()->Sign(packet, size, signature); - p->len = size; - m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); - LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); - } + p->len = size; + m_Service.post(std::bind(&Stream::SendPacket, shared_from_this(), p)); + LogPrint(eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); + } - size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) - { - size_t pos = 0; - while (pos < len && !m_ReceiveQueue.empty ()) - { - Packet * packet = m_ReceiveQueue.front (); - size_t l = std::min (packet->GetLength (), len - pos); - memcpy (buf + pos, packet->GetBuffer (), l); - pos += l; - packet->offset += l; - if (!packet->GetLength ()) - { - m_ReceiveQueue.pop (); - m_LocalDestination.DeletePacket (packet); - } - } - return pos; - } + size_t Stream::ConcatenatePackets(uint8_t *buf, size_t len) { + size_t pos = 0; + while (pos < len && !m_ReceiveQueue.empty()) { + Packet *packet = m_ReceiveQueue.front(); + size_t l = std::min(packet->GetLength(), len - pos); + memcpy(buf + pos, packet->GetBuffer(), l); + pos += l; + packet->offset += l; + if (!packet->GetLength()) { + m_ReceiveQueue.pop(); + m_LocalDestination.DeletePacket(packet); + } + } + return pos; + } - bool Stream::SendPacket (Packet * packet) - { - if (packet) - { - if (m_IsAckSendScheduled) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - SendPackets (std::vector { packet }); - bool isEmpty = m_SentPackets.empty (); - m_SentPackets.insert (packet); - if (isEmpty) - ScheduleResend (); - return true; - } - else - return false; - } + bool Stream::SendPacket(Packet *packet) { + if (packet) { + if (m_IsAckSendScheduled) { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel(); + } + SendPackets(std::vector{packet}); + bool isEmpty = m_SentPackets.empty(); + m_SentPackets.insert(packet); + if (isEmpty) + ScheduleResend(); + return true; + } else + return false; + } - void Stream::SendPackets (const std::vector& packets) - { - if (!m_RemoteLeaseSet) - { - UpdateCurrentRemoteLease (); - if (!m_RemoteLeaseSet) - { - LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID); - return; - } - } - if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) // expired and detached or new session sent - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); - if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send - { - // try to get shared path first - auto routingPath = m_RoutingSession->GetSharedRoutingPath (); - if (routingPath) - { - m_CurrentOutboundTunnel = routingPath->outboundTunnel; - m_CurrentRemoteLease = routingPath->remoteLease; - m_RTT = routingPath->rtt; - m_RTO = m_RTT*1.5; // TODO: implement it better - } - } + void Stream::SendPackets(const std::vector &packets) { + if (!m_RemoteLeaseSet) { + UpdateCurrentRemoteLease(); + if (!m_RemoteLeaseSet) { + LogPrint(eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", + m_SendStreamID); + return; + } + } + if (!m_RoutingSession || m_RoutingSession->IsTerminated() || + !m_RoutingSession->IsReadyToSend()) // expired and detached or new session sent + m_RoutingSession = m_LocalDestination.GetOwner()->GetRoutingSession(m_RemoteLeaseSet, true); + if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send + { + // try to get shared path first + auto routingPath = m_RoutingSession->GetSharedRoutingPath(); + if (routingPath) { + m_CurrentOutboundTunnel = routingPath->outboundTunnel; + m_CurrentRemoteLease = routingPath->remoteLease; + m_RTT = routingPath->rtt; + m_RTO = m_RTT * 1.5; // TODO: implement it better + } + } - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet - ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) - UpdateCurrentRemoteLease (true); - if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) - { - if (!m_CurrentOutboundTunnel) - { - auto leaseRouter = i2p::data::netdb.FindRouter (m_CurrentRemoteLease->tunnelGateway); - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (nullptr, - leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); - } - else if (!m_CurrentOutboundTunnel->IsEstablished ()) - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); - if (!m_CurrentOutboundTunnel) - { - LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); - m_CurrentRemoteLease = nullptr; - return; - } + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet + ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) + UpdateCurrentRemoteLease(true); + if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { + if (!m_CurrentOutboundTunnel) { + auto leaseRouter = i2p::data::netdb.FindRouter(m_CurrentRemoteLease->tunnelGateway); + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner()->GetTunnelPool()->GetNextOutboundTunnel( + nullptr, + leaseRouter ? leaseRouter->GetCompatibleTransports(false) + : (i2p::data::RouterInfo::CompatibleTransports) i2p::data::RouterInfo::eAllTransports); + } else if (!m_CurrentOutboundTunnel->IsEstablished()) + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner()->GetTunnelPool()->GetNewOutboundTunnel( + m_CurrentOutboundTunnel); + if (!m_CurrentOutboundTunnel) { + LogPrint(eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); + m_CurrentRemoteLease = nullptr; + return; + } - std::vector msgs; - for (const auto& it: packets) - { - auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage ( - it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ())); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeTunnel, - m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID, - msg - }); - m_NumSentBytes += it->GetLength (); - } - m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); - } - else - { - LogPrint (eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID); - if (m_RoutingSession) - m_RoutingSession->SetSharedRoutingPath (nullptr); // invalidate routing path - } - } + std::vector msgs; + for (const auto &it: packets) { + auto msg = m_RoutingSession->WrapSingleMessage(m_LocalDestination.CreateDataMessage( + it->GetBuffer(), it->GetLength(), m_Port, !m_RoutingSession->IsRatchets())); + msgs.push_back(i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID, + msg + }); + m_NumSentBytes += it->GetLength(); + } + m_CurrentOutboundTunnel->SendTunnelDataMsg(msgs); + } else { + LogPrint(eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID); + if (m_RoutingSession) + m_RoutingSession->SetSharedRoutingPath(nullptr); // invalidate routing path + } + } - void Stream::SendUpdatedLeaseSet () - { - if (m_RoutingSession && !m_RoutingSession->IsTerminated ()) - { - if (m_RoutingSession->IsLeaseSetNonConfirmed ()) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT) - { - // LeaseSet was not confirmed, should try other tunnels - LogPrint (eLogWarning, "Streaming: LeaseSet was not confirmed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); - m_RoutingSession->SetSharedRoutingPath (nullptr); - m_CurrentOutboundTunnel = nullptr; - m_CurrentRemoteLease = nullptr; - SendQuickAck (); - } - } - else if (m_RoutingSession->IsLeaseSetUpdated ()) - { - LogPrint (eLogDebug, "Streaming: sending updated LeaseSet"); - SendQuickAck (); - } - } - else - SendQuickAck (); - } + void Stream::SendUpdatedLeaseSet() { + if (m_RoutingSession && !m_RoutingSession->IsTerminated()) { + if (m_RoutingSession->IsLeaseSetNonConfirmed()) { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + if (ts > m_RoutingSession->GetLeaseSetSubmissionTime() + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT) { + // LeaseSet was not confirmed, should try other tunnels + LogPrint(eLogWarning, "Streaming: LeaseSet was not confirmed in ", + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); + m_RoutingSession->SetSharedRoutingPath(nullptr); + m_CurrentOutboundTunnel = nullptr; + m_CurrentRemoteLease = nullptr; + SendQuickAck(); + } + } else if (m_RoutingSession->IsLeaseSetUpdated()) { + LogPrint(eLogDebug, "Streaming: sending updated LeaseSet"); + SendQuickAck(); + } + } else + SendQuickAck(); + } - void Stream::ScheduleResend () - { - if (m_Status != eStreamStatusTerminated) - { - m_ResendTimer.cancel (); - // check for invalid value - if (m_RTO <= 0) m_RTO = INITIAL_RTO; - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); - m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, - shared_from_this (), std::placeholders::_1)); - } - } + void Stream::ScheduleResend() { + if (m_Status != eStreamStatusTerminated) { + m_ResendTimer.cancel(); + // check for invalid value + if (m_RTO <= 0) m_RTO = INITIAL_RTO; + m_ResendTimer.expires_from_now(boost::posix_time::milliseconds(m_RTO)); + m_ResendTimer.async_wait(std::bind(&Stream::HandleResendTimer, + shared_from_this(), std::placeholders::_1)); + } + } - void Stream::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - // check for resend attempts - if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) - { - LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } + void Stream::HandleResendTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + // check for resend attempts + if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) { + LogPrint(eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, + " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); + m_Status = eStreamStatusReset; + Close(); + return; + } - // collect packets to resend - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector packets; - for (auto it : m_SentPackets) - { - if (ts >= it->sendTime + m_RTO) - { - it->sendTime = ts; - packets.push_back (it); - } - } + // collect packets to resend + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + std::vector packets; + for (auto it: m_SentPackets) { + if (ts >= it->sendTime + m_RTO) { + it->sendTime = ts; + packets.push_back(it); + } + } - // select tunnels if necessary and send - if (packets.size () > 0) - { - m_NumResendAttempts++; - m_RTO *= 2; - switch (m_NumResendAttempts) - { - case 1: // congesion avoidance - m_WindowSize >>= 1; // /2 - if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; - break; - case 2: - m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time + // select tunnels if necessary and send + if (packets.size() > 0) { + m_NumResendAttempts++; + m_RTO *= 2; + switch (m_NumResendAttempts) { + case 1: // congesion avoidance + m_WindowSize >>= 1; // /2 + if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; + break; + case 2: + m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; + [[fallthrough]]; #endif - // no break here - case 4: - if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); - UpdateCurrentRemoteLease (); // pick another lease - LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - break; - case 3: - // pick another outbound tunnel - if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); - break; - default: ; - } - SendPackets (packets); - } - ScheduleResend (); - } - } + // no break here + case 4: + if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath(nullptr); + UpdateCurrentRemoteLease(); // pick another lease + LogPrint(eLogWarning, + "Streaming: Another remote lease has been selected for stream with rSID=", + m_RecvStreamID, ", sSID=", m_SendStreamID); + break; + case 3: + // pick another outbound tunnel + if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath(nullptr); + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner()->GetTunnelPool()->GetNextOutboundTunnel( + m_CurrentOutboundTunnel); + LogPrint(eLogWarning, + "Streaming: Another outbound tunnel has been selected for stream with sSID=", + m_SendStreamID); + break; + default:; + } + SendPackets(packets); + } + ScheduleResend(); + } + } - void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) - { - if (m_IsAckSendScheduled) - { - if (m_LastReceivedSequenceNumber < 0) - { - LogPrint (eLogWarning, "Streaming: SYN has not been received after ", SYN_TIMEOUT, " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } - if (m_Status == eStreamStatusOpen) - { - if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ()) - { - // seems something went wrong and we should re-select tunnels - m_CurrentOutboundTunnel = nullptr; - m_CurrentRemoteLease = nullptr; - } - SendQuickAck (); - } - m_IsAckSendScheduled = false; - } - } + void Stream::HandleAckSendTimer(const boost::system::error_code &ecode) { + if (m_IsAckSendScheduled) { + if (m_LastReceivedSequenceNumber < 0) { + LogPrint(eLogWarning, "Streaming: SYN has not been received after ", SYN_TIMEOUT, + " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", + m_SendStreamID); + m_Status = eStreamStatusReset; + Close(); + return; + } + if (m_Status == eStreamStatusOpen) { + if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed()) { + // seems something went wrong and we should re-select tunnels + m_CurrentOutboundTunnel = nullptr; + m_CurrentRemoteLease = nullptr; + } + SendQuickAck(); + } + m_IsAckSendScheduled = false; + } + } - void Stream::UpdateCurrentRemoteLease (bool expired) - { - if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) - { - auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); - if (!remoteLeaseSet) - { - LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), m_RemoteLeaseSet ? " expired" : " not found"); - if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ()) - { - m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( - std::make_shared(m_RemoteIdentity)); - return; // we keep m_RemoteLeaseSet for possible next request - } - else - { - m_RemoteLeaseSet = nullptr; - m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt - } - } - else - { - // LeaseSet updated - m_RemoteLeaseSet = remoteLeaseSet; - m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); - m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); - } - } - if (m_RemoteLeaseSet) - { - if (!m_RoutingSession) - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first - if (leases.empty ()) - { - expired = false; - // time to request - if (m_RemoteLeaseSet->IsPublishedEncrypted ()) - m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( - std::make_shared(m_RemoteIdentity)); - else - m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); - leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold - } - if (!leases.empty ()) - { - bool updated = false; - if (expired && m_CurrentRemoteLease) - { - for (const auto& it: leases) - if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID)) - { - m_CurrentRemoteLease = it; - updated = true; - break; - } - } - if (!updated) - { - uint32_t i = rand () % leases.size (); - if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) - // make sure we don't select previous - i = (i + 1) % leases.size (); // if so, pick next - m_CurrentRemoteLease = leases[i]; - } - } - else - { - LogPrint (eLogWarning, "Streaming: All remote leases are expired"); - m_RemoteLeaseSet = nullptr; - m_CurrentRemoteLease = nullptr; - // we have requested expired before, no need to do it twice - } - } - else - { - LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found"); - m_CurrentRemoteLease = nullptr; - } - } + void Stream::UpdateCurrentRemoteLease(bool expired) { + if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired()) { + auto remoteLeaseSet = m_LocalDestination.GetOwner()->FindLeaseSet(m_RemoteIdentity->GetIdentHash()); + if (!remoteLeaseSet) { + LogPrint(eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash().ToBase64(), + m_RemoteLeaseSet ? " expired" : " not found"); + if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted()) { + m_LocalDestination.GetOwner()->RequestDestinationWithEncryptedLeaseSet( + std::make_shared(m_RemoteIdentity)); + return; // we keep m_RemoteLeaseSet for possible next request + } else { + m_RemoteLeaseSet = nullptr; + m_LocalDestination.GetOwner()->RequestDestination( + m_RemoteIdentity->GetIdentHash()); // try to request for a next attempt + } + } else { + // LeaseSet updated + m_RemoteLeaseSet = remoteLeaseSet; + m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity(); + m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier(); + } + } + if (m_RemoteLeaseSet) { + if (!m_RoutingSession) + m_RoutingSession = m_LocalDestination.GetOwner()->GetRoutingSession(m_RemoteLeaseSet, true); + auto leases = m_RemoteLeaseSet->GetNonExpiredLeases(false); // try without threshold first + if (leases.empty()) { + expired = false; + // time to request + if (m_RemoteLeaseSet->IsPublishedEncrypted()) + m_LocalDestination.GetOwner()->RequestDestinationWithEncryptedLeaseSet( + std::make_shared(m_RemoteIdentity)); + else + m_LocalDestination.GetOwner()->RequestDestination(m_RemoteIdentity->GetIdentHash()); + leases = m_RemoteLeaseSet->GetNonExpiredLeases(true); // then with threshold + } + if (!leases.empty()) { + bool updated = false; + if (expired && m_CurrentRemoteLease) { + for (const auto &it: leases) + if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && + (it->tunnelID != m_CurrentRemoteLease->tunnelID)) { + m_CurrentRemoteLease = it; + updated = true; + break; + } + } + if (!updated) { + uint32_t i = rand() % leases.size(); + if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) + // make sure we don't select previous + i = (i + 1) % leases.size(); // if so, pick next + m_CurrentRemoteLease = leases[i]; + } + } else { + LogPrint(eLogWarning, "Streaming: All remote leases are expired"); + m_RemoteLeaseSet = nullptr; + m_CurrentRemoteLease = nullptr; + // we have requested expired before, no need to do it twice + } + } else { + LogPrint(eLogWarning, "Streaming: Remote LeaseSet not found"); + m_CurrentRemoteLease = nullptr; + } + } - StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): - m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), - m_PendingIncomingTimer (m_Owner->GetService ()) - { - if (m_Gzip) - m_Deflator.reset (new i2p::data::GzipDeflator); - } + StreamingDestination::StreamingDestination(std::shared_ptr owner, + uint16_t localPort, bool gzip) : + m_Owner(owner), m_LocalPort(localPort), m_Gzip(gzip), + m_PendingIncomingTimer(m_Owner->GetService()) { + if (m_Gzip) + m_Deflator.reset(new i2p::data::GzipDeflator); + } - StreamingDestination::~StreamingDestination () - { - for (auto& it: m_SavedPackets) - { - for (auto it1: it.second) DeletePacket (it1); - it.second.clear (); - } - m_SavedPackets.clear (); - } + StreamingDestination::~StreamingDestination() { + for (auto &it: m_SavedPackets) { + for (auto it1: it.second) DeletePacket(it1); + it.second.clear(); + } + m_SavedPackets.clear(); + } - void StreamingDestination::Start () - { - } + void StreamingDestination::Start() { + } - void StreamingDestination::Stop () - { - ResetAcceptor (); - m_PendingIncomingTimer.cancel (); - m_PendingIncomingStreams.clear (); - { - std::unique_lock l(m_StreamsMutex); - for (auto it: m_Streams) - it.second->Terminate (false); // we delete here - m_Streams.clear (); - m_IncomingStreams.clear (); - m_LastStream = nullptr; - } - } + void StreamingDestination::Stop() { + ResetAcceptor(); + m_PendingIncomingTimer.cancel(); + m_PendingIncomingStreams.clear(); + { + std::unique_lock l(m_StreamsMutex); + for (auto it: m_Streams) + it.second->Terminate(false); // we delete here + m_Streams.clear(); + m_IncomingStreams.clear(); + m_LastStream = nullptr; + } + } - void StreamingDestination::HandleNextPacket (Packet * packet) - { - uint32_t sendStreamID = packet->GetSendStreamID (); - if (sendStreamID) - { - if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ()) - { - auto it = m_Streams.find (sendStreamID); - if (it != m_Streams.end ()) - m_LastStream = it->second; - else - m_LastStream = nullptr; - } - if (m_LastStream) - m_LastStream->HandleNextPacket (packet); - else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ()) - { - // ping - LogPrint (eLogInfo, "Streaming: Ping received sSID=", sendStreamID); - auto s = std::make_shared (m_Owner->GetService (), *this); - s->HandlePing (packet); - } - else - { - LogPrint (eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID); - DeletePacket (packet); - } - } - else - { - if (packet->IsEcho ()) - { - // pong - LogPrint (eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID ()); - DeletePacket (packet); - return; - } - if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream - { - uint32_t receiveStreamID = packet->GetReceiveStreamID (); - auto it1 = m_IncomingStreams.find (receiveStreamID); - if (it1 != m_IncomingStreams.end ()) - { - // already pending - LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); - DeletePacket (packet); // drop it, because previous should be connected - return; - } - auto incomingStream = CreateNewIncomingStream (receiveStreamID); - incomingStream->HandleNextPacket (packet); // SYN - auto ident = incomingStream->GetRemoteIdentity(); + void StreamingDestination::HandleNextPacket(Packet *packet) { + uint32_t sendStreamID = packet->GetSendStreamID(); + if (sendStreamID) { + if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID()) { + auto it = m_Streams.find(sendStreamID); + if (it != m_Streams.end()) + m_LastStream = it->second; + else + m_LastStream = nullptr; + } + if (m_LastStream) + m_LastStream->HandleNextPacket(packet); + else if (packet->IsEcho() && m_Owner->IsStreamingAnswerPings()) { + // ping + LogPrint(eLogInfo, "Streaming: Ping received sSID=", sendStreamID); + auto s = std::make_shared(m_Owner->GetService(), *this); + s->HandlePing(packet); + } else { + LogPrint(eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID); + DeletePacket(packet); + } + } else { + if (packet->IsEcho()) { + // pong + LogPrint(eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID()); + DeletePacket(packet); + return; + } + if (packet->IsSYN() && !packet->GetSeqn()) // new incoming stream + { + uint32_t receiveStreamID = packet->GetReceiveStreamID(); + auto it1 = m_IncomingStreams.find(receiveStreamID); + if (it1 != m_IncomingStreams.end()) { + // already pending + LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, + " already exists"); + DeletePacket(packet); // drop it, because previous should be connected + return; + } + auto incomingStream = CreateNewIncomingStream(receiveStreamID); + incomingStream->HandleNextPacket(packet); // SYN + auto ident = incomingStream->GetRemoteIdentity(); - // handle saved packets if any - { - auto it = m_SavedPackets.find (receiveStreamID); - if (it != m_SavedPackets.end ()) - { - LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID); - for (auto it1: it->second) - incomingStream->HandleNextPacket (it1); - m_SavedPackets.erase (it); - } - } - // accept - if (m_Acceptor != nullptr) - m_Acceptor (incomingStream); - else - { - LogPrint (eLogWarning, "Streaming: Acceptor for incoming stream is not set"); - if (m_PendingIncomingStreams.size () < MAX_PENDING_INCOMING_BACKLOG) - { - m_PendingIncomingStreams.push_back (incomingStream); - m_PendingIncomingTimer.cancel (); - m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); - m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer, - shared_from_this (), std::placeholders::_1)); - LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID); - } - else - { - LogPrint (eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", MAX_PENDING_INCOMING_BACKLOG); - incomingStream->Close (); - } - } - } - else // follow on packet without SYN - { - uint32_t receiveStreamID = packet->GetReceiveStreamID (); - auto it1 = m_IncomingStreams.find (receiveStreamID); - if (it1 != m_IncomingStreams.end ()) - { - // found - it1->second->HandleNextPacket (packet); - return; - } - // save follow on packet - auto it = m_SavedPackets.find (receiveStreamID); - if (it != m_SavedPackets.end ()) - it->second.push_back (packet); - else - { - m_SavedPackets[receiveStreamID] = std::list{ packet }; - auto timer = std::make_shared (m_Owner->GetService ()); - timer->expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); - auto s = shared_from_this (); - timer->async_wait ([s,timer,receiveStreamID](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto it = s->m_SavedPackets.find (receiveStreamID); - if (it != s->m_SavedPackets.end ()) - { - for (auto it1: it->second) s->DeletePacket (it1); - it->second.clear (); - s->m_SavedPackets.erase (it); - } - } - }); - } - } - } - } + // handle saved packets if any + { + auto it = m_SavedPackets.find(receiveStreamID); + if (it != m_SavedPackets.end()) { + LogPrint(eLogDebug, "Streaming: Processing ", it->second.size(), " saved packets for rSID=", + receiveStreamID); + for (auto it1: it->second) + incomingStream->HandleNextPacket(it1); + m_SavedPackets.erase(it); + } + } + // accept + if (m_Acceptor != nullptr) + m_Acceptor(incomingStream); + else { + LogPrint(eLogWarning, "Streaming: Acceptor for incoming stream is not set"); + if (m_PendingIncomingStreams.size() < MAX_PENDING_INCOMING_BACKLOG) { + m_PendingIncomingStreams.push_back(incomingStream); + m_PendingIncomingTimer.cancel(); + m_PendingIncomingTimer.expires_from_now( + boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); + m_PendingIncomingTimer.async_wait( + std::bind(&StreamingDestination::HandlePendingIncomingTimer, + shared_from_this(), std::placeholders::_1)); + LogPrint(eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID); + } else { + LogPrint(eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", + MAX_PENDING_INCOMING_BACKLOG); + incomingStream->Close(); + } + } + } else // follow on packet without SYN + { + uint32_t receiveStreamID = packet->GetReceiveStreamID(); + auto it1 = m_IncomingStreams.find(receiveStreamID); + if (it1 != m_IncomingStreams.end()) { + // found + it1->second->HandleNextPacket(packet); + return; + } + // save follow on packet + auto it = m_SavedPackets.find(receiveStreamID); + if (it != m_SavedPackets.end()) + it->second.push_back(packet); + else { + m_SavedPackets[receiveStreamID] = std::list{packet}; + auto timer = std::make_shared(m_Owner->GetService()); + timer->expires_from_now(boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); + auto s = shared_from_this(); + timer->async_wait([s, timer, receiveStreamID](const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto it = s->m_SavedPackets.find(receiveStreamID); + if (it != s->m_SavedPackets.end()) { + for (auto it1: it->second) s->DeletePacket(it1); + it->second.clear(); + s->m_SavedPackets.erase(it); + } + } + }); + } + } + } + } - std::shared_ptr StreamingDestination::CreateNewOutgoingStream (std::shared_ptr remote, int port) - { - auto s = std::make_shared (m_Owner->GetService (), *this, remote, port); - std::unique_lock l(m_StreamsMutex); - m_Streams.emplace (s->GetRecvStreamID (), s); - return s; - } + std::shared_ptr + StreamingDestination::CreateNewOutgoingStream(std::shared_ptr remote, int port) { + auto s = std::make_shared(m_Owner->GetService(), *this, remote, port); + std::unique_lock l(m_StreamsMutex); + m_Streams.emplace(s->GetRecvStreamID(), s); + return s; + } - void StreamingDestination::SendPing (std::shared_ptr remote) - { - auto s = std::make_shared (m_Owner->GetService (), *this, remote, 0); - s->SendPing (); - } + void StreamingDestination::SendPing(std::shared_ptr remote) { + auto s = std::make_shared(m_Owner->GetService(), *this, remote, 0); + s->SendPing(); + } - std::shared_ptr StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID) - { - auto s = std::make_shared (m_Owner->GetService (), *this); - std::unique_lock l(m_StreamsMutex); - m_Streams.emplace (s->GetRecvStreamID (), s); - m_IncomingStreams.emplace (receiveStreamID, s); - return s; - } + std::shared_ptr StreamingDestination::CreateNewIncomingStream(uint32_t receiveStreamID) { + auto s = std::make_shared(m_Owner->GetService(), *this); + std::unique_lock l(m_StreamsMutex); + m_Streams.emplace(s->GetRecvStreamID(), s); + m_IncomingStreams.emplace(receiveStreamID, s); + return s; + } - void StreamingDestination::DeleteStream (std::shared_ptr stream) - { - if (stream) - { - std::unique_lock l(m_StreamsMutex); - m_Streams.erase (stream->GetRecvStreamID ()); - m_IncomingStreams.erase (stream->GetSendStreamID ()); - if (m_LastStream == stream) m_LastStream = nullptr; - } - if (m_Streams.empty ()) - { - m_PacketsPool.CleanUp (); - m_I2NPMsgsPool.CleanUp (); - } - } + void StreamingDestination::DeleteStream(std::shared_ptr stream) { + if (stream) { + std::unique_lock l(m_StreamsMutex); + m_Streams.erase(stream->GetRecvStreamID()); + m_IncomingStreams.erase(stream->GetSendStreamID()); + if (m_LastStream == stream) m_LastStream = nullptr; + } + if (m_Streams.empty()) { + m_PacketsPool.CleanUp(); + m_I2NPMsgsPool.CleanUp(); + } + } - bool StreamingDestination::DeleteStream (uint32_t recvStreamID) - { - auto it = m_Streams.find (recvStreamID); - if (it == m_Streams.end ()) - return false; - auto s = it->second; - m_Owner->GetService ().post ([this, s] () - { - s->Close (); // try to send FIN - s->Terminate (false); - DeleteStream (s); - }); - return true; - } + bool StreamingDestination::DeleteStream(uint32_t recvStreamID) { + auto it = m_Streams.find(recvStreamID); + if (it == m_Streams.end()) + return false; + auto s = it->second; + m_Owner->GetService().post([this, s]() { + s->Close(); // try to send FIN + s->Terminate(false); + DeleteStream(s); + }); + return true; + } - void StreamingDestination::SetAcceptor (const Acceptor& acceptor) - { - m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet - auto s = shared_from_this (); - m_Owner->GetService ().post([s](void) - { - // take care about incoming queue - for (auto& it: s->m_PendingIncomingStreams) - if (it->GetStatus () == eStreamStatusOpen) // still open? - s->m_Acceptor (it); - s->m_PendingIncomingStreams.clear (); - s->m_PendingIncomingTimer.cancel (); - }); - } + void StreamingDestination::SetAcceptor(const Acceptor &acceptor) { + m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet + auto s = shared_from_this(); + m_Owner->GetService().post([s](void) { + // take care about incoming queue + for (auto &it: s->m_PendingIncomingStreams) + if (it->GetStatus() == eStreamStatusOpen) // still open? + s->m_Acceptor(it); + s->m_PendingIncomingStreams.clear(); + s->m_PendingIncomingTimer.cancel(); + }); + } - void StreamingDestination::ResetAcceptor () - { - if (m_Acceptor) m_Acceptor (nullptr); - m_Acceptor = nullptr; - } + void StreamingDestination::ResetAcceptor() { + if (m_Acceptor) m_Acceptor(nullptr); + m_Acceptor = nullptr; + } - void StreamingDestination::AcceptOnce (const Acceptor& acceptor) - { - m_Owner->GetService ().post([acceptor, this](void) - { - if (!m_PendingIncomingStreams.empty ()) - { - acceptor (m_PendingIncomingStreams.front ()); - m_PendingIncomingStreams.pop_front (); - if (m_PendingIncomingStreams.empty ()) - m_PendingIncomingTimer.cancel (); - } - else // we must save old acceptor and set it back - { - m_Acceptor = std::bind (&StreamingDestination::AcceptOnceAcceptor, this, - std::placeholders::_1, acceptor, m_Acceptor); - } - }); - } + void StreamingDestination::AcceptOnce(const Acceptor &acceptor) { + m_Owner->GetService().post([acceptor, this](void) { + if (!m_PendingIncomingStreams.empty()) { + acceptor(m_PendingIncomingStreams.front()); + m_PendingIncomingStreams.pop_front(); + if (m_PendingIncomingStreams.empty()) + m_PendingIncomingTimer.cancel(); + } else // we must save old acceptor and set it back + { + m_Acceptor = std::bind(&StreamingDestination::AcceptOnceAcceptor, this, + std::placeholders::_1, acceptor, m_Acceptor); + } + }); + } - void StreamingDestination::AcceptOnceAcceptor (std::shared_ptr stream, Acceptor acceptor, Acceptor prev) - { - m_Acceptor = prev; - acceptor (stream); - } + void + StreamingDestination::AcceptOnceAcceptor(std::shared_ptr stream, Acceptor acceptor, Acceptor prev) { + m_Acceptor = prev; + acceptor(stream); + } - void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired"); - for (auto& it: m_PendingIncomingStreams) - it->Close (); - m_PendingIncomingStreams.clear (); - } - } + void StreamingDestination::HandlePendingIncomingTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogWarning, "Streaming: Pending incoming timeout expired"); + for (auto &it: m_PendingIncomingStreams) + it->Close(); + m_PendingIncomingStreams.clear(); + } + } - void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) - { - // unzip it - Packet * uncompressed = NewPacket (); - uncompressed->offset = 0; - uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE); - if (uncompressed->len) - HandleNextPacket (uncompressed); - else - DeletePacket (uncompressed); - } + void StreamingDestination::HandleDataMessagePayload(const uint8_t *buf, size_t len) { + // unzip it + Packet *uncompressed = NewPacket(); + uncompressed->offset = 0; + uncompressed->len = m_Inflator.Inflate(buf, len, uncompressed->buf, MAX_PACKET_SIZE); + if (uncompressed->len) + HandleNextPacket(uncompressed); + else + DeletePacket(uncompressed); + } - std::shared_ptr StreamingDestination::CreateDataMessage ( - const uint8_t * payload, size_t len, uint16_t toPort, bool checksum) - { - size_t size; - auto msg = m_I2NPMsgsPool.AcquireShared (); - uint8_t * buf = msg->GetPayload (); - buf += 4; // reserve for lengthlength - msg->len += 4; + std::shared_ptr StreamingDestination::CreateDataMessage( + const uint8_t *payload, size_t len, uint16_t toPort, bool checksum) { + size_t size; + auto msg = m_I2NPMsgsPool.AcquireShared(); + uint8_t *buf = msg->GetPayload(); + buf += 4; // reserve for lengthlength + msg->len += 4; - if (m_Gzip && m_Deflator) - size = m_Deflator->Deflate (payload, len, buf, msg->maxLen - msg->len); - else - size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len); + if (m_Gzip && m_Deflator) + size = m_Deflator->Deflate(payload, len, buf, msg->maxLen - msg->len); + else + size = i2p::data::GzipNoCompression(payload, len, buf, msg->maxLen - msg->len); - if (size) - { - htobe32buf (msg->GetPayload (), size); // length - htobe16buf (buf + 4, m_LocalPort); // source port - htobe16buf (buf + 6, toPort); // destination port - buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol - msg->len += size; - msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); - } - else - msg = nullptr; - return msg; - } + if (size) { + htobe32buf(msg->GetPayload(), size); // length + htobe16buf(buf + 4, m_LocalPort); // source port + htobe16buf(buf + 6, toPort); // destination port + buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol + msg->len += size; + msg->FillI2NPMessageHeader(eI2NPData, 0, checksum); + } else + msg = nullptr; + return msg; + } -} + } } diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 088c2e11..0a90e16a 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -27,353 +27,419 @@ #include "Tunnel.h" #include "util.h" // MemoryPool -namespace i2p -{ -namespace client -{ - class ClientDestination; -} -namespace stream -{ - const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; - const uint16_t PACKET_FLAG_CLOSE = 0x0002; - const uint16_t PACKET_FLAG_RESET = 0x0004; - const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; - const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; - const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; - const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; - const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; - const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; - const uint16_t PACKET_FLAG_ECHO = 0x0200; - const uint16_t PACKET_FLAG_NO_ACK = 0x0400; - const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800; +namespace i2p { + namespace client { + class ClientDestination; + } + namespace stream { + const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; + const uint16_t PACKET_FLAG_CLOSE = 0x0002; + const uint16_t PACKET_FLAG_RESET = 0x0004; + const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; + const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; + const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; + const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; + const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; + const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; + 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_RATCHETS = 1812; - const size_t MAX_PACKET_SIZE = 4096; - const size_t COMPRESSION_THRESHOLD_SIZE = 66; - const int MAX_NUM_RESEND_ATTEMPTS = 6; - const int WINDOW_SIZE = 6; // in messages - const int MIN_WINDOW_SIZE = 1; - const int MAX_WINDOW_SIZE = 128; - const int INITIAL_RTT = 8000; // in milliseconds - const int INITIAL_RTO = 9000; // 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 size_t MAX_PENDING_INCOMING_BACKLOG = 128; - const int PENDING_INCOMING_TIMEOUT = 10; // in seconds - const int MAX_RECEIVE_TIMEOUT = 20; // in seconds + const size_t STREAMING_MTU = 1730; + const size_t STREAMING_MTU_RATCHETS = 1812; + const size_t MAX_PACKET_SIZE = 4096; + const size_t COMPRESSION_THRESHOLD_SIZE = 66; + const int MAX_NUM_RESEND_ATTEMPTS = 6; + const int WINDOW_SIZE = 6; // in messages + const int MIN_WINDOW_SIZE = 1; + const int MAX_WINDOW_SIZE = 128; + const int INITIAL_RTT = 8000; // in milliseconds + const int INITIAL_RTO = 9000; // 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 size_t MAX_PENDING_INCOMING_BACKLOG = 128; + const int PENDING_INCOMING_TIMEOUT = 10; // in seconds + const int MAX_RECEIVE_TIMEOUT = 20; // in seconds - struct Packet - { - size_t len, offset; - uint8_t buf[MAX_PACKET_SIZE]; - uint64_t sendTime; + struct Packet { + size_t len, offset; + uint8_t buf[MAX_PACKET_SIZE]; + uint64_t sendTime; - Packet (): len (0), offset (0), sendTime (0) {}; - uint8_t * GetBuffer () { return buf + offset; }; - size_t GetLength () const { return len - offset; }; + Packet() : len(0), offset(0), sendTime(0) {}; - uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; - 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 (); }; + uint8_t *GetBuffer() { return buf + offset; }; - bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; - bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; }; - bool IsEcho () const { return GetFlags () & PACKET_FLAG_ECHO; }; - }; + size_t GetLength() const { return len - offset; }; - struct PacketCmp - { - bool operator() (const Packet * p1, const Packet * p2) const - { - return p1->GetSeqn () < p2->GetSeqn (); - }; - }; + uint32_t GetSendStreamID() const { return bufbe32toh(buf); }; - typedef std::function SendHandler; - struct SendBuffer - { - uint8_t * buf; - size_t len, offset; - SendHandler handler; + uint32_t GetReceiveStreamID() const { return bufbe32toh(buf + 4); }; - SendBuffer (const uint8_t * b, size_t l, SendHandler h): - 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; }; - }; + uint32_t GetSeqn() const { return bufbe32toh(buf + 8); }; - class SendBufferQueue - { - public: + uint32_t GetAckThrough() const { return bufbe32toh(buf + 12); }; - SendBufferQueue (): m_Size (0) {}; - ~SendBufferQueue () { CleanUp (); }; + uint8_t GetNACKCount() const { return buf[16]; }; - void Add (const uint8_t * buf, size_t len, SendHandler handler); - void Add (std::shared_ptr 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 (); + uint32_t GetNACK(int i) const { return bufbe32toh(buf + 17 + 4 * i); }; - 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 > m_Buffers; - size_t m_Size; - }; + uint16_t GetOptionSize() const { return bufbe16toh(GetOption()); }; - enum StreamStatus - { - eStreamStatusNew = 0, - eStreamStatusOpen, - eStreamStatusReset, - eStreamStatusClosing, - eStreamStatusClosed, - eStreamStatusTerminated - }; + const uint8_t *GetOptionData() const { return GetOption() + 2; }; - class StreamingDestination; - class Stream: public std::enable_shared_from_this - { - public: + const uint8_t *GetPayload() const { return GetOptionData() + GetOptionSize(); }; - Stream (boost::asio::io_service& service, StreamingDestination& local, - std::shared_ptr remote, int port = 0); // outgoing - Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming + bool IsSYN() const { return GetFlags() & PACKET_FLAG_SYNCHRONIZE; }; - ~Stream (); - uint32_t GetSendStreamID () const { return m_SendStreamID; }; - uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; - std::shared_ptr GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; - std::shared_ptr 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; }; + bool IsNoAck() const { return GetFlags() & PACKET_FLAG_NO_ACK; }; - 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 (); + bool IsEcho() const { return GetFlags() & PACKET_FLAG_ECHO; }; + }; - template - void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); - size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; + struct PacketCmp { + bool operator()(const Packet *p1, const Packet *p2) const { + return p1->GetSeqn() < p2->GetSeqn(); + }; + }; - void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; + typedef std::function SendHandler; - /** only call close from destination thread, use Stream::AsyncClose for other threads */ - void Close (); - void Cancel () { m_ReceiveTimer.cancel (); }; + struct SendBuffer { + uint8_t *buf; + size_t len, offset; + SendHandler handler; - 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; }; + SendBuffer(const uint8_t *b, size_t l, SendHandler h) : + len(l), offset(0), handler(h) { + buf = new uint8_t[len]; + memcpy(buf, b, len); + } - 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 (); - void SendQuickAck (); - void SendClose (); - bool SendPacket (Packet * packet); - void SendPackets (const std::vector& packets); - void SendUpdatedLeaseSet (); + const uint8_t *GetRemaningBuffer() const { return buf + offset; }; - 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 Cancel() { + if (handler) + handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted)); + handler = nullptr; + }; + }; - void UpdateCurrentRemoteLease (bool expired = false); + class SendBufferQueue { + public: - template - void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout); + SendBufferQueue() : m_Size(0) {}; - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); - void HandleAckSendTimer (const boost::system::error_code& ecode); + ~SendBufferQueue() { CleanUp(); }; - private: + void Add(const uint8_t *buf, size_t len, SendHandler handler); - 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 m_RemoteIdentity; - std::shared_ptr m_TransientVerifier; // in case of offline key - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_RoutingSession; - std::shared_ptr m_CurrentRemoteLease; - std::shared_ptr m_CurrentOutboundTunnel; - std::queue m_ReceiveQueue; - std::set m_SavedPackets; - std::set m_SentPackets; - boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer; - size_t m_NumSentBytes, m_NumReceivedBytes; - uint16_t m_Port; + void Add(std::shared_ptr buf); - 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; - }; + size_t Get(uint8_t *buf, size_t len); - class StreamingDestination: public std::enable_shared_from_this - { - public: + size_t GetSize() const { return m_Size; }; - typedef std::function)> Acceptor; + bool IsEmpty() const { return m_Buffers.empty(); }; - StreamingDestination (std::shared_ptr owner, uint16_t localPort = 0, bool gzip = false); - ~StreamingDestination (); + void CleanUp(); - void Start (); - void Stop (); + private: - std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); - void SendPing (std::shared_ptr remote); - void DeleteStream (std::shared_ptr 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, Acceptor acceptor, Acceptor prev); + std::list > m_Buffers; + size_t m_Size; + }; - std::shared_ptr GetOwner () const { return m_Owner; }; - void SetOwner (std::shared_ptr owner) { m_Owner = owner; }; - uint16_t GetLocalPort () const { return m_LocalPort; }; + enum StreamStatus { + eStreamStatusNew = 0, + eStreamStatusOpen, + eStreamStatusReset, + eStreamStatusClosing, + eStreamStatusClosed, + eStreamStatusTerminated + }; - void HandleDataMessagePayload (const uint8_t * buf, size_t len); - std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true); + class StreamingDestination; - Packet * NewPacket () { return m_PacketsPool.Acquire(); } - void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } + class Stream : public std::enable_shared_from_this { + public: - private: + Stream(boost::asio::io_service &service, StreamingDestination &local, + std::shared_ptr remote, int port = 0); // outgoing + Stream(boost::asio::io_service &service, StreamingDestination &local); // incoming - void HandleNextPacket (Packet * packet); - std::shared_ptr CreateNewIncomingStream (uint32_t receiveStreamID); - void HandlePendingIncomingTimer (const boost::system::error_code& ecode); + ~Stream(); - private: + uint32_t GetSendStreamID() const { return m_SendStreamID; }; - std::shared_ptr m_Owner; - uint16_t m_LocalPort; - bool m_Gzip; // gzip compression of data messages - std::mutex m_StreamsMutex; - std::unordered_map > m_Streams; // sendStreamID->stream - std::unordered_map > m_IncomingStreams; // receiveStreamID->stream - std::shared_ptr m_LastStream; - Acceptor m_Acceptor; - std::list > m_PendingIncomingStreams; - boost::asio::deadline_timer m_PendingIncomingTimer; - std::unordered_map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN + uint32_t GetRecvStreamID() const { return m_RecvStreamID; }; - i2p::util::MemoryPool m_PacketsPool; - i2p::util::MemoryPool > m_I2NPMsgsPool; + std::shared_ptr GetRemoteLeaseSet() const { return m_RemoteLeaseSet; }; - public: + std::shared_ptr GetRemoteIdentity() const { return m_RemoteIdentity; }; - i2p::data::GzipInflator m_Inflator; - std::unique_ptr m_Deflator; + bool IsOpen() const { return m_Status == eStreamStatusOpen; }; - // for HTTP only - const decltype(m_Streams)& GetStreams () const { return m_Streams; }; - }; + bool IsEstablished() const { return m_SendStreamID; }; + + 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 + 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 &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 + 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 m_RemoteIdentity; + std::shared_ptr m_TransientVerifier; // in case of offline key + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_RoutingSession; + std::shared_ptr m_CurrentRemoteLease; + std::shared_ptr m_CurrentOutboundTunnel; + std::queue m_ReceiveQueue; + std::set m_SavedPackets; + std::set 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 { + public: + + typedef std::function)> Acceptor; + + StreamingDestination(std::shared_ptr owner, uint16_t localPort = 0, + bool gzip = false); + + ~StreamingDestination(); + + void Start(); + + void Stop(); + + std::shared_ptr + CreateNewOutgoingStream(std::shared_ptr remote, int port = 0); + + void SendPing(std::shared_ptr remote); + + void DeleteStream(std::shared_ptr 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, Acceptor acceptor, Acceptor prev); + + std::shared_ptr GetOwner() const { return m_Owner; }; + + void SetOwner(std::shared_ptr owner) { m_Owner = owner; }; + + uint16_t GetLocalPort() const { return m_LocalPort; }; + + void HandleDataMessagePayload(const uint8_t *buf, size_t len); + + std::shared_ptr + CreateDataMessage(const uint8_t *payload, size_t len, uint16_t toPort, 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 CreateNewIncomingStream(uint32_t receiveStreamID); + + void HandlePendingIncomingTimer(const boost::system::error_code &ecode); + + private: + + std::shared_ptr m_Owner; + uint16_t m_LocalPort; + bool m_Gzip; // gzip compression of data messages + std::mutex m_StreamsMutex; + std::unordered_map > m_Streams; // sendStreamID->stream + std::unordered_map > m_IncomingStreams; // receiveStreamID->stream + std::shared_ptr m_LastStream; + Acceptor m_Acceptor; + std::list > m_PendingIncomingStreams; + boost::asio::deadline_timer m_PendingIncomingTimer; + std::unordered_map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN + + i2p::util::MemoryPool m_PacketsPool; + i2p::util::MemoryPool > m_I2NPMsgsPool; + + public: + + i2p::data::GzipInflator m_Inflator; + std::unique_ptr m_Deflator; + + // for HTTP only + const decltype(m_Streams) + & + + GetStreams() const { return m_Streams; }; + }; //------------------------------------------------- - template - void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) - { - auto s = shared_from_this(); - 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), buffer, handler, 0); - else - { - int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout; - s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t)); - int left = timeout - t; - auto self = s->shared_from_this(); - self->m_ReceiveTimer.async_wait ( - [self, buffer, handler, left](const boost::system::error_code & ec) - { - self->HandleReceiveTimer(ec, buffer, handler, left); - }); - } - }); - } + template + void Stream::AsyncReceive(const Buffer &buffer, ReceiveHandler handler, int timeout) { + auto s = shared_from_this(); + 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), + buffer, handler, 0); + else { + int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout; + s->m_ReceiveTimer.expires_from_now(boost::posix_time::seconds(t)); + int left = timeout - t; + auto self = s->shared_from_this(); + self->m_ReceiveTimer.async_wait( + [self, buffer, handler, left](const boost::system::error_code &ec) { + self->HandleReceiveTimer(ec, buffer, handler, left); + }); + } + }); + } - template - void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout) - { - size_t received = ConcatenatePackets (boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); - if (received > 0) - handler (boost::system::error_code (), received); - else if (ecode == boost::asio::error::operation_aborted) - { - // timeout not expired - if (m_Status == eStreamStatusReset) - handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0); - else - handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0); - } - else - { - // timeout expired - if (remainingTimeout <= 0) - handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); - else - { - // itermediate interrupt - SendUpdatedLeaseSet (); // send our leaseset if applicable - AsyncReceive (buffer, handler, remainingTimeout); - } - } - } -} + template + void + Stream::HandleReceiveTimer(const boost::system::error_code &ecode, const Buffer &buffer, ReceiveHandler handler, + int remainingTimeout) { + size_t received = ConcatenatePackets(boost::asio::buffer_cast(buffer), + boost::asio::buffer_size(buffer)); + if (received > 0) + handler(boost::system::error_code(), received); + else if (ecode == boost::asio::error::operation_aborted) { + // timeout not expired + if (m_Status == eStreamStatusReset) + handler(boost::asio::error::make_error_code(boost::asio::error::connection_reset), 0); + else + handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted), 0); + } else { + // timeout expired + if (remainingTimeout <= 0) + handler(boost::asio::error::make_error_code(boost::asio::error::timed_out), received); + else { + // itermediate interrupt + SendUpdatedLeaseSet(); // send our leaseset if applicable + AsyncReceive(buffer, handler, remainingTimeout); + } + } + } + } } #endif diff --git a/libi2pd/Tag.h b/libi2pd/Tag.h index d898395f..1df723d1 100644 --- a/libi2pd/Tag.h +++ b/libi2pd/Tag.h @@ -15,92 +15,89 @@ #include "Base.h" namespace i2p { -namespace data { - template - class Tag - { - BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes"); + namespace data { + template + class Tag { + BOOST_STATIC_ASSERT_MSG(sz + % 8 == 0, "Tag size must be multiple of 8 bytes"); - public: + public: - Tag () = default; - Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); } + Tag() = default; - bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); } - bool operator!= (const Tag& other) const { return !(*this == other); } - bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; } + Tag(const uint8_t *buf) { memcpy(m_Buf, buf, sz); } - uint8_t * operator()() { return m_Buf; } - const uint8_t * operator()() const { return m_Buf; } + bool operator==(const Tag &other) const { return !memcmp(m_Buf, other.m_Buf, sz); } - operator uint8_t * () { return m_Buf; } - operator const uint8_t * () const { return m_Buf; } + bool operator!=(const Tag &other) const { return !(*this == other); } - const uint8_t * data() const { return m_Buf; } - const uint64_t * GetLL () const { return ll; } + bool operator<(const Tag &other) const { return memcmp(m_Buf, other.m_Buf, sz) < 0; } - bool IsZero () const - { - for (size_t i = 0; i < sz/8; ++i) - if (ll[i]) return false; - return true; - } + uint8_t *operator()() { return m_Buf; } - void Fill(uint8_t c) - { - memset(m_Buf, c, sz); - } + const uint8_t *operator()() const { return m_Buf; } - void Randomize() - { - RAND_bytes(m_Buf, sz); - } + operator uint8_t *() { return m_Buf; } - std::string ToBase64 (size_t len = sz) const - { - char str[sz*2]; - size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2); - return std::string (str, str + l); - } + operator const uint8_t *() const { return m_Buf; } - 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); - } + const uint8_t *data() const { return m_Buf; } - size_t FromBase32 (const std::string& s) - { - return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } + const uint64_t *GetLL() const { return ll; } - size_t FromBase64 (const std::string& s) - { - return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } + bool IsZero() const { + for (size_t i = 0; i < sz / 8; ++i) + if (ll[i]) return false; + return true; + } - private: + void Fill(uint8_t c) { + memset(m_Buf, c, sz); + } - union // 8 bytes aligned - { - uint8_t m_Buf[sz]; - uint64_t ll[sz/8]; - }; - }; -} // data + void Randomize() { + RAND_bytes(m_Buf, sz); + } + + std::string ToBase64(size_t len = sz) const { + 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 { + 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 -namespace std -{ - // hash for std::unordered_map - template struct hash > - { - size_t operator()(const i2p::data::Tag& s) const - { - return s.GetLL ()[0]; - } - }; +namespace std { + // hash for std::unordered_map + template + struct hash > { + size_t operator()(const i2p::data::Tag &s) const { + return s.GetLL()[0]; + } + }; } #endif /* TAG_H__ */ diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index 99507398..eb0fd12a 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -22,238 +22,196 @@ #include "util.h" #ifdef _WIN32 - #ifndef _WIN64 - #define _USE_32BIT_TIME_T - #endif +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif #endif -namespace i2p -{ -namespace util -{ - static uint64_t GetLocalMillisecondsSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } +namespace i2p { + namespace util { + static uint64_t GetLocalMillisecondsSinceEpoch() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } - static uint64_t GetLocalSecondsSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } + static uint64_t GetLocalSecondsSinceEpoch() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } - static uint32_t GetLocalMinutesSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } + static uint32_t GetLocalMinutesSinceEpoch() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } - static uint32_t GetLocalHoursSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } + static uint32_t GetLocalHoursSinceEpoch() { + return std::chrono::duration_cast( + 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) - { - LogPrint (eLogInfo, "Timestamp: NTP request to ", address); - boost::asio::io_service service; - boost::system::error_code ec; - auto it = boost::asio::ip::udp::resolver (service).resolve ( - boost::asio::ip::udp::resolver::query (address, "ntp"), ec); - if (!ec) - { - bool found = false; - boost::asio::ip::udp::resolver::iterator end; - boost::asio::ip::udp::endpoint ep; - while (it != end) - { - ep = *it; - if (!ep.address ().is_unspecified ()) - { - if (ep.address ().is_v4 ()) - { - if (i2p::context.SupportsV4 ()) found = true; - } - else if (ep.address ().is_v6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (ep.address ())) - { - if (i2p::context.SupportsMesh ()) found = true; - } - else if (i2p::context.SupportsV6 ()) found = true; - } - } - if (found) break; - it++; - } - if (!found) - { - LogPrint (eLogError, "Timestamp: can't find compatible address for ", address); - return; - } + static void SyncTimeWithNTP(const std::string &address) { + LogPrint(eLogInfo, "Timestamp: NTP request to ", address); + boost::asio::io_service service; + boost::system::error_code ec; + auto it = boost::asio::ip::udp::resolver(service).resolve( + boost::asio::ip::udp::resolver::query(address, "ntp"), ec); + if (!ec) { + bool found = false; + boost::asio::ip::udp::resolver::iterator end; + boost::asio::ip::udp::endpoint ep; + while (it != end) { + ep = *it; + if (!ep.address().is_unspecified()) { + if (ep.address().is_v4()) { + if (i2p::context.SupportsV4()) found = true; + } else if (ep.address().is_v6()) { + if (i2p::util::net::IsYggdrasilAddress(ep.address())) { + if (i2p::context.SupportsMesh()) found = true; + } 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); - socket.open (ep.protocol (), ec); - if (!ec) - { - uint8_t buf[48];// 48 bytes NTP request/response - memset (buf, 0, 48); - htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330 - size_t len = 0; - try - { - socket.send_to (boost::asio::buffer (buf, 48), ep); - int i = 0; - while (!socket.available() && i < 10) // 10 seconds max - { - std::this_thread::sleep_for (std::chrono::seconds(1)); - i++; - } - if (socket.available ()) - len = socket.receive_from (boost::asio::buffer (buf, 48), ep); - } - catch (std::exception& e) - { - LogPrint (eLogError, "Timestamp: NTP error: ", e.what ()); - } - if (len >= 8) - { - auto ourTs = GetLocalSecondsSinceEpoch (); - uint32_t ts = bufbe32toh (buf + 32); - if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900 - g_TimeOffset = ts - ourTs; - LogPrint (eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset, " seconds"); - } - } - else - LogPrint (eLogError, "Timestamp: Couldn't open UDP socket"); - } - else - LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address); - } + boost::asio::ip::udp::socket socket(service); + socket.open(ep.protocol(), ec); + if (!ec) { + uint8_t buf[48];// 48 bytes NTP request/response + memset(buf, 0, 48); + htobe32buf(buf, (3 << 27) | (3 << 24)); // RFC 4330 + size_t len = 0; + try { + socket.send_to(boost::asio::buffer(buf, 48), ep); + int i = 0; + while (!socket.available() && i < 10) // 10 seconds max + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + i++; + } + if (socket.available()) + len = socket.receive_from(boost::asio::buffer(buf, 48), ep); + } + catch (std::exception &e) { + LogPrint(eLogError, "Timestamp: NTP error: ", e.what()); + } + if (len >= 8) { + auto ourTs = GetLocalSecondsSinceEpoch(); + uint32_t ts = bufbe32toh(buf + 32); + if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900 + g_TimeOffset = ts - ourTs; + LogPrint(eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset, + " seconds"); + } + } 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) - { - i2p::config::GetOption("nettime.ntpsyncinterval", m_SyncInterval); - std::string ntpservers; i2p::config::GetOption("nettime.ntpservers", ntpservers); - boost::split (m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on); - } + NTPTimeSync::NTPTimeSync() : m_IsRunning(false), m_Timer(m_Service) { + i2p::config::GetOption("nettime.ntpsyncinterval", m_SyncInterval); + std::string ntpservers; + i2p::config::GetOption("nettime.ntpservers", ntpservers); + boost::split(m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on); + } - NTPTimeSync::~NTPTimeSync () - { - Stop (); - } + NTPTimeSync::~NTPTimeSync() { + Stop(); + } - void NTPTimeSync::Start() - { - if (m_NTPServersList.size () > 0) - { - m_IsRunning = true; - LogPrint(eLogInfo, "Timestamp: NTP time sync starting"); - m_Service.post (std::bind (&NTPTimeSync::Sync, this)); - m_Thread.reset (new std::thread (std::bind (&NTPTimeSync::Run, this))); - } - else - LogPrint (eLogWarning, "Timestamp: No NTP server found"); - } + void NTPTimeSync::Start() { + if (m_NTPServersList.size() > 0) { + m_IsRunning = true; + LogPrint(eLogInfo, "Timestamp: NTP time sync starting"); + m_Service.post(std::bind(&NTPTimeSync::Sync, this)); + m_Thread.reset(new std::thread(std::bind(&NTPTimeSync::Run, this))); + } else + LogPrint(eLogWarning, "Timestamp: No NTP server found"); + } - void NTPTimeSync::Stop () - { - if (m_IsRunning) - { - LogPrint(eLogInfo, "Timestamp: NTP time sync stopping"); - m_IsRunning = false; - m_Timer.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread.reset (nullptr); - } - } - } + void NTPTimeSync::Stop() { + if (m_IsRunning) { + LogPrint(eLogInfo, "Timestamp: NTP time sync stopping"); + m_IsRunning = false; + m_Timer.cancel(); + m_Service.stop(); + if (m_Thread) { + m_Thread->join(); + m_Thread.reset(nullptr); + } + } + } - void NTPTimeSync::Run () - { - i2p::util::SetThreadName("Timesync"); + void NTPTimeSync::Run() { + i2p::util::SetThreadName("Timesync"); - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Timestamp: NTP time sync exception: ", ex.what ()); - } - } - } + while (m_IsRunning) { + try { + m_Service.run(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "Timestamp: NTP time sync exception: ", ex.what()); + } + } + } - void NTPTimeSync::Sync () - { - if (m_NTPServersList.size () > 0) - SyncTimeWithNTP (m_NTPServersList[rand () % m_NTPServersList.size ()]); - else - m_IsRunning = false; + void NTPTimeSync::Sync() { + if (m_NTPServersList.size() > 0) + SyncTimeWithNTP(m_NTPServersList[rand() % m_NTPServersList.size()]); + else + m_IsRunning = false; - if (m_IsRunning) - { - m_Timer.expires_from_now (boost::posix_time::hours (m_SyncInterval)); - m_Timer.async_wait ([this](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - Sync (); - }); - } - } + if (m_IsRunning) { + m_Timer.expires_from_now(boost::posix_time::hours(m_SyncInterval)); + m_Timer.async_wait([this](const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) + Sync(); + }); + } + } - uint64_t GetMillisecondsSinceEpoch () - { - return GetLocalMillisecondsSinceEpoch () + g_TimeOffset*1000; - } + uint64_t GetMillisecondsSinceEpoch() { + return GetLocalMillisecondsSinceEpoch() + g_TimeOffset * 1000; + } - uint64_t GetSecondsSinceEpoch () - { - return GetLocalSecondsSinceEpoch () + g_TimeOffset; - } + uint64_t GetSecondsSinceEpoch() { + return GetLocalSecondsSinceEpoch() + g_TimeOffset; + } - uint32_t GetMinutesSinceEpoch () - { - return GetLocalMinutesSinceEpoch () + g_TimeOffset/60; - } + uint32_t GetMinutesSinceEpoch() { + return GetLocalMinutesSinceEpoch() + g_TimeOffset / 60; + } - uint32_t GetHoursSinceEpoch () - { - return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; - } + uint32_t GetHoursSinceEpoch() { + return GetLocalHoursSinceEpoch() + g_TimeOffset / 3600; + } - void GetCurrentDate (char * date) - { - GetDateString (GetSecondsSinceEpoch (), date); - } + void GetCurrentDate(char *date) { + GetDateString(GetSecondsSinceEpoch(), date); + } - void GetDateString (uint64_t timestamp, char * date) - { - using clock = std::chrono::system_clock; - auto t = clock::to_time_t (clock::time_point (std::chrono::seconds(timestamp))); - struct tm tm; + void GetDateString(uint64_t timestamp, char *date) { + using clock = std::chrono::system_clock; + auto t = clock::to_time_t(clock::time_point(std::chrono::seconds(timestamp))); + struct tm tm; #ifdef _WIN32 - gmtime_s(&tm, &t); - sprintf_s(date, 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + gmtime_s(&tm, &t); + sprintf_s(date, 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); #else - gmtime_r(&t, &tm); - sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + gmtime_r(&t, &tm); + sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); #endif - } + } - void AdjustTimeOffset (int64_t offset) - { - g_TimeOffset += offset; - } -} + void AdjustTimeOffset(int64_t offset) { + g_TimeOffset += offset; + } + } } diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 995ea36f..02d587db 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -15,44 +15,48 @@ #include #include -namespace i2p -{ -namespace util -{ - uint64_t GetMillisecondsSinceEpoch (); - uint64_t GetSecondsSinceEpoch (); - uint32_t GetMinutesSinceEpoch (); - uint32_t GetHoursSinceEpoch (); +namespace i2p { + namespace util { + uint64_t GetMillisecondsSinceEpoch(); - void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes - void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes - void AdjustTimeOffset (int64_t offset); // in seconds from current + uint64_t GetSecondsSinceEpoch(); - class NTPTimeSync - { - public: + uint32_t GetMinutesSinceEpoch(); - NTPTimeSync (); - ~NTPTimeSync (); + uint32_t GetHoursSinceEpoch(); - void Start (); - void Stop (); + void GetCurrentDate(char *date); // returns date as YYYYMMDD string, 9 bytes + void GetDateString(uint64_t timestamp, + char *date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes + void AdjustTimeOffset(int64_t offset); // in seconds from current - private: + class NTPTimeSync { + public: - void Run (); - void Sync (); + NTPTimeSync(); - private: + ~NTPTimeSync(); - bool m_IsRunning; - std::unique_ptr m_Thread; - boost::asio::io_service m_Service; - boost::asio::deadline_timer m_Timer; - int m_SyncInterval; - std::vector m_NTPServersList; - }; -} + void Start(); + + void Stop(); + + private: + + void Run(); + + void Sync(); + + private: + + bool m_IsRunning; + std::unique_ptr m_Thread; + boost::asio::io_service m_Service; + boost::asio::deadline_timer m_Timer; + int m_SyncInterval; + std::vector m_NTPServersList; + }; + } } #endif diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index c4f3fa19..09c17568 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -15,104 +15,88 @@ #include "Transports.h" #include "TransitTunnel.h" -namespace i2p -{ -namespace tunnel -{ - TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey): - TunnelBase (receiveTunnelID, nextTunnelID, nextIdent) - { - m_Encryption.SetKeys (layerKey, ivKey); - } +namespace i2p { + namespace tunnel { + TransitTunnel::TransitTunnel(uint32_t receiveTunnelID, + const uint8_t *nextIdent, uint32_t nextTunnelID, + const uint8_t *layerKey, const uint8_t *ivKey) : + TunnelBase(receiveTunnelID, nextTunnelID, nextIdent) { + m_Encryption.SetKeys(layerKey, ivKey); + } - void TransitTunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) - { - m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); - i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE); - } + void TransitTunnel::EncryptTunnelMsg(std::shared_ptr in, std::shared_ptr out) { + m_Encryption.Encrypt(in->GetPayload() + 4, out->GetPayload() + 4); + i2p::transport::transports.UpdateTotalTransitTransmittedBytes(TUNNEL_DATA_MSG_SIZE); + } - TransitTunnelParticipant::~TransitTunnelParticipant () - { - } + TransitTunnelParticipant::~TransitTunnelParticipant() { + } - void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - EncryptTunnelMsg (tunnelMsg, tunnelMsg); + void TransitTunnelParticipant::HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg) { + EncryptTunnelMsg(tunnelMsg, tunnelMsg); - m_NumTransmittedBytes += tunnelMsg->GetLength (); - htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ()); - tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); - m_TunnelDataMsgs.push_back (tunnelMsg); - } + m_NumTransmittedBytes += tunnelMsg->GetLength(); + htobe32buf(tunnelMsg->GetPayload(), GetNextTunnelID()); + tunnelMsg->FillI2NPMessageHeader(eI2NPTunnelData); + m_TunnelDataMsgs.push_back(tunnelMsg); + } - void TransitTunnelParticipant::FlushTunnelDataMsgs () - { - if (!m_TunnelDataMsgs.empty ()) - { - auto num = m_TunnelDataMsgs.size (); - if (num > 1) - LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num); - i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); - m_TunnelDataMsgs.clear (); - } - } + void TransitTunnelParticipant::FlushTunnelDataMsgs() { + if (!m_TunnelDataMsgs.empty()) { + auto num = m_TunnelDataMsgs.size(); + if (num > 1) + LogPrint(eLogDebug, "TransitTunnel: ", GetTunnelID(), "->", GetNextTunnelID(), " ", num); + i2p::transport::transports.SendMessages(GetNextIdentHash(), m_TunnelDataMsgs); + m_TunnelDataMsgs.clear(); + } + } - void TransitTunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); - } + void TransitTunnel::SendTunnelDataMsg(std::shared_ptr msg) { + LogPrint(eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID()); + } - void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); - } + void TransitTunnel::HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg) { + LogPrint(eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID()); + } - void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr msg) - { - TunnelMessageBlock block; - block.deliveryType = eDeliveryTypeLocal; - block.data = msg; - std::unique_lock l(m_SendMutex); - m_Gateway.PutTunnelDataMsg (block); - } + void TransitTunnelGateway::SendTunnelDataMsg(std::shared_ptr msg) { + TunnelMessageBlock block; + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + std::unique_lock l(m_SendMutex); + m_Gateway.PutTunnelDataMsg(block); + } - void TransitTunnelGateway::FlushTunnelDataMsgs () - { - std::unique_lock l(m_SendMutex); - m_Gateway.SendBuffer (); - } + void TransitTunnelGateway::FlushTunnelDataMsgs() { + std::unique_lock l(m_SendMutex); + m_Gateway.SendBuffer(); + } - void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - auto newMsg = CreateEmptyTunnelDataMsg (true); - EncryptTunnelMsg (tunnelMsg, newMsg); + void TransitTunnelEndpoint::HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg) { + auto newMsg = CreateEmptyTunnelDataMsg(true); + EncryptTunnelMsg(tunnelMsg, newMsg); - LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); - m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); - } + LogPrint(eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID()); + m_Endpoint.HandleDecryptedTunnelDataMsg(newMsg); + } - std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey, - bool isGateway, bool isEndpoint) - { - if (isEndpoint) - { - LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); - return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - else if (isGateway) - { - LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created"); - return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - else - { - LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); - return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - } -} + std::shared_ptr CreateTransitTunnel(uint32_t receiveTunnelID, + const uint8_t *nextIdent, uint32_t nextTunnelID, + const uint8_t *layerKey, const uint8_t *ivKey, + bool isGateway, bool isEndpoint) { + if (isEndpoint) { + LogPrint(eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); + return std::make_shared(receiveTunnelID, nextIdent, nextTunnelID, layerKey, + ivKey); + } else if (isGateway) { + LogPrint(eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created"); + return std::make_shared(receiveTunnelID, nextIdent, nextTunnelID, layerKey, + ivKey); + } else { + LogPrint(eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); + return std::make_shared(receiveTunnelID, nextIdent, nextTunnelID, layerKey, + ivKey); + } + } + } } diff --git a/libi2pd/TransitTunnel.h b/libi2pd/TransitTunnel.h index bce90958..79ffb3ea 100644 --- a/libi2pd/TransitTunnel.h +++ b/libi2pd/TransitTunnel.h @@ -19,95 +19,98 @@ #include "TunnelGateway.h" #include "TunnelBase.h" -namespace i2p -{ -namespace tunnel -{ - class TransitTunnel: public TunnelBase - { - public: +namespace i2p { + namespace tunnel { + class TransitTunnel : public TunnelBase { + public: - TransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey); + TransitTunnel(uint32_t receiveTunnelID, + const uint8_t *nextIdent, uint32_t nextTunnelID, + const uint8_t *layerKey, const uint8_t *ivKey); - virtual size_t GetNumTransmittedBytes () const { return 0; }; + virtual size_t GetNumTransmittedBytes() const { return 0; }; - // implements TunnelBase - void SendTunnelDataMsg (std::shared_ptr msg); - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); - void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); - private: + // implements TunnelBase + void SendTunnelDataMsg(std::shared_ptr msg); - i2p::crypto::TunnelEncryption m_Encryption; - }; + void HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg); - class TransitTunnelParticipant: public TransitTunnel - { - public: + void EncryptTunnelMsg(std::shared_ptr in, std::shared_ptr out); - TransitTunnelParticipant (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_NumTransmittedBytes (0) {}; - ~TransitTunnelParticipant (); + private: - size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); - void FlushTunnelDataMsgs (); + i2p::crypto::TunnelEncryption m_Encryption; + }; - private: + class TransitTunnelParticipant : public TransitTunnel { + public: - size_t m_NumTransmittedBytes; - std::vector > m_TunnelDataMsgs; - }; + TransitTunnelParticipant(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_NumTransmittedBytes(0) {}; - class TransitTunnelGateway: public TransitTunnel - { - public: + ~TransitTunnelParticipant(); - 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) {}; + size_t GetNumTransmittedBytes() const { return m_NumTransmittedBytes; }; - void SendTunnelDataMsg (std::shared_ptr msg); - void FlushTunnelDataMsgs (); - size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; + void HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg); - private: + void FlushTunnelDataMsgs(); - std::mutex m_SendMutex; - TunnelGateway m_Gateway; - }; + private: - class TransitTunnelEndpoint: public TransitTunnel - { - public: + size_t m_NumTransmittedBytes; + std::vector > m_TunnelDataMsgs; + }; - 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 + class TransitTunnelGateway : public TransitTunnel { + public: - 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&& tunnelMsg); - size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } + void SendTunnelDataMsg(std::shared_ptr msg); - private: + void FlushTunnelDataMsgs(); - TunnelEndpoint m_Endpoint; - }; + size_t GetNumTransmittedBytes() const { return m_Gateway.GetNumSentBytes(); }; - std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey, - bool isGateway, bool isEndpoint); -} + private: + + std::mutex m_SendMutex; + 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 &&tunnelMsg); + + size_t GetNumTransmittedBytes() const { return m_Endpoint.GetNumReceivedBytes(); } + + private: + + TunnelEndpoint m_Endpoint; + }; + + std::shared_ptr CreateTransitTunnel(uint32_t receiveTunnelID, + const uint8_t *nextIdent, uint32_t nextTunnelID, + const uint8_t *layerKey, const uint8_t *ivKey, + bool isGateway, bool isEndpoint); + } } #endif diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 53192816..ad967aec 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -20,111 +20,112 @@ #include "I2NPProtocol.h" #include "Timestamp.h" -namespace i2p -{ -namespace transport -{ - const size_t IPV4_HEADER_SIZE = 20; - const size_t IPV6_HEADER_SIZE = 40; - const size_t UDP_HEADER_SIZE = 8; - - class SignedData - { - public: +namespace i2p { + namespace transport { + const size_t IPV4_HEADER_SIZE = 20; + const size_t IPV6_HEADER_SIZE = 40; + const size_t UDP_HEADER_SIZE = 8; - SignedData () {} - SignedData (const SignedData& other) - { - m_Stream << other.m_Stream.rdbuf (); - } + class SignedData { + public: - void Reset () - { - m_Stream.str(""); - } - - void Insert (const uint8_t * buf, size_t len) - { - m_Stream.write ((char *)buf, len); - } + SignedData() {} - template - void Insert (T t) - { - m_Stream.write ((char *)&t, sizeof (T)); - } + SignedData(const SignedData &other) { + m_Stream << other.m_Stream.rdbuf(); + } - bool Verify (std::shared_ptr ident, const uint8_t * signature) const - { - return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); - } + void Reset() { + m_Stream.str(""); + } - void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const - { - keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); - } + void Insert(const uint8_t *buf, size_t len) { + m_Stream.write((char *) buf, len); + } - private: + template + void Insert(T t) { + m_Stream.write((char *) &t, sizeof(T)); + } - std::stringstream m_Stream; - }; - - class TransportSession - { - public: + bool Verify(std::shared_ptr ident, const uint8_t *signature) const { + return ident->Verify((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature); + } - TransportSession (std::shared_ptr router, int terminationTimeout): - m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), - m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()) - { - if (router) - m_RemoteIdentity = router->GetRouterIdentity (); - m_CreationTime = m_LastActivityTimestamp; - } + void Sign(const i2p::data::PrivateKeys &keys, uint8_t *signature) const { + keys.Sign((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature); + } - virtual ~TransportSession () {}; - virtual void Done () = 0; + private: - std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; } + std::stringstream m_Stream; + }; - std::shared_ptr GetRemoteIdentity () - { - std::lock_guard l(m_RemoteIdentityMutex); - return m_RemoteIdentity; - } - void SetRemoteIdentity (std::shared_ptr ident) - { - std::lock_guard l(m_RemoteIdentityMutex); - m_RemoteIdentity = ident; - } + class TransportSession { + public: - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - bool IsOutgoing () const { return m_IsOutgoing; }; + TransportSession(std::shared_ptr router, int terminationTimeout) : + m_NumSentBytes(0), m_NumReceivedBytes(0), m_IsOutgoing(router), + m_TerminationTimeout(terminationTimeout), + m_LastActivityTimestamp(i2p::util::GetSecondsSinceEpoch()) { + if (router) + m_RemoteIdentity = router->GetRouterIdentity(); + m_CreationTime = m_LastActivityTimestamp; + } - int GetTerminationTimeout () const { return m_TerminationTimeout; }; - void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; }; - bool IsTerminationTimeoutExpired (uint64_t ts) const - { return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); }; + virtual ~TransportSession() {}; - 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 >& msgs) = 0; + virtual void Done() = 0; - protected: + std::string GetIdentHashBase64() const { + return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; + } - std::shared_ptr 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 - }; -} + std::shared_ptr GetRemoteIdentity() { + std::lock_guard l(m_RemoteIdentityMutex); + return m_RemoteIdentity; + } + + void SetRemoteIdentity(std::shared_ptr ident) { + std::lock_guard 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 > &msgs) = 0; + + protected: + + std::shared_ptr 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 diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index b37ef7b2..2de5174e 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -18,962 +18,834 @@ using namespace i2p::data; -namespace i2p -{ -namespace transport -{ - template - EphemeralKeysSupplier::EphemeralKeysSupplier (int size): - m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr) - { - } +namespace i2p { + namespace transport { + template + EphemeralKeysSupplier::EphemeralKeysSupplier(int size): + m_QueueSize(size), m_IsRunning(false), m_Thread(nullptr) { + } - template - EphemeralKeysSupplier::~EphemeralKeysSupplier () - { - Stop (); - } + template + EphemeralKeysSupplier::~EphemeralKeysSupplier() { + Stop(); + } - template - void EphemeralKeysSupplier::Start () - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&EphemeralKeysSupplier::Run, this)); - } + template + void EphemeralKeysSupplier::Start() { + m_IsRunning = true; + m_Thread = new std::thread(std::bind(&EphemeralKeysSupplier::Run, this)); + } - template - void EphemeralKeysSupplier::Stop () - { - { - std::unique_lock l(m_AcquiredMutex); - m_IsRunning = false; - m_Acquired.notify_one (); - } - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - } + template + void EphemeralKeysSupplier::Stop() { + { + std::unique_lock l(m_AcquiredMutex); + m_IsRunning = false; + m_Acquired.notify_one(); + } + if (m_Thread) { + m_Thread->join(); + delete m_Thread; + m_Thread = 0; + } + } - template - void EphemeralKeysSupplier::Run () - { - i2p::util::SetThreadName("Ephemerals"); + template + void EphemeralKeysSupplier::Run() { + i2p::util::SetThreadName("Ephemerals"); - while (m_IsRunning) - { - int num, total = 0; - while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 10) - { - CreateEphemeralKeys (num); - total += num; - } - if (total >= 10) - { - LogPrint (eLogWarning, "Transports: ", total, " ephemeral keys generated at the time"); - std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break - } - else - { - std::unique_lock l(m_AcquiredMutex); - if (!m_IsRunning) break; - m_Acquired.wait (l); // wait for element gets acquired - } - } - } + while (m_IsRunning) { + int num, total = 0; + while ((num = m_QueueSize - (int) m_Queue.size()) > 0 && total < 10) { + CreateEphemeralKeys(num); + total += num; + } + if (total >= 10) { + LogPrint(eLogWarning, "Transports: ", total, " ephemeral keys generated at the time"); + std::this_thread::sleep_for(std::chrono::seconds(1)); // take a break + } else { + std::unique_lock l(m_AcquiredMutex); + if (!m_IsRunning) break; + m_Acquired.wait(l); // wait for element gets acquired + } + } + } - template - void EphemeralKeysSupplier::CreateEphemeralKeys (int num) - { - if (num > 0) - { - for (int i = 0; i < num; i++) - { - auto pair = std::make_shared (); - pair->GenerateKeys (); - std::unique_lock l(m_AcquiredMutex); - m_Queue.push (pair); - } - } - } + template + void EphemeralKeysSupplier::CreateEphemeralKeys(int num) { + if (num > 0) { + for (int i = 0; i < num; i++) { + auto pair = std::make_shared(); + pair->GenerateKeys(); + std::unique_lock l(m_AcquiredMutex); + m_Queue.push(pair); + } + } + } - template - std::shared_ptr EphemeralKeysSupplier::Acquire () - { - { - std::unique_lock l(m_AcquiredMutex); - if (!m_Queue.empty ()) - { - auto pair = m_Queue.front (); - m_Queue.pop (); - m_Acquired.notify_one (); - return pair; - } - } - // queue is empty, create new - auto pair = std::make_shared (); - pair->GenerateKeys (); - return pair; - } + template + std::shared_ptr EphemeralKeysSupplier::Acquire() { + { + std::unique_lock l(m_AcquiredMutex); + if (!m_Queue.empty()) { + auto pair = m_Queue.front(); + m_Queue.pop(); + m_Acquired.notify_one(); + return pair; + } + } + // queue is empty, create new + auto pair = std::make_shared(); + pair->GenerateKeys(); + return pair; + } - template - void EphemeralKeysSupplier::Return (std::shared_ptr pair) - { - if (pair) - { - std::unique_lockl(m_AcquiredMutex); - if ((int)m_Queue.size () < 2*m_QueueSize) - m_Queue.push (pair); - } - else - LogPrint(eLogError, "Transports: Return null DHKeys"); - } + template + void EphemeralKeysSupplier::Return(std::shared_ptr pair) { + if (pair) { + std::unique_lock l(m_AcquiredMutex); + if ((int) m_Queue.size() < 2 * m_QueueSize) + m_Queue.push(pair); + } else + LogPrint(eLogError, "Transports: Return null DHKeys"); + } - Transports transports; + Transports transports; - Transports::Transports (): - m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr), - m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_SSUServer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr), - m_X25519KeysPairSupplier (15), // 15 pre-generated keys - m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), - m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), - m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), - m_LastTransitBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0) - { - } + Transports::Transports() : + m_IsOnline(true), m_IsRunning(false), m_IsNAT(true), m_CheckReserved(true), m_Thread(nullptr), + m_Service(nullptr), m_Work(nullptr), m_PeerCleanupTimer(nullptr), m_PeerTestTimer(nullptr), + m_SSUServer(nullptr), m_SSU2Server(nullptr), m_NTCP2Server(nullptr), + m_X25519KeysPairSupplier(15), // 15 pre-generated keys + m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes(0), + m_InBandwidth(0), m_OutBandwidth(0), m_TransitBandwidth(0), + m_LastInBandwidthUpdateBytes(0), m_LastOutBandwidthUpdateBytes(0), + m_LastTransitBandwidthUpdateBytes(0), m_LastBandwidthUpdateTime(0) { + } - Transports::~Transports () - { - Stop (); - if (m_Service) - { - delete m_PeerCleanupTimer; m_PeerCleanupTimer = nullptr; - delete m_PeerTestTimer; m_PeerTestTimer = nullptr; - delete m_Work; m_Work = nullptr; - delete m_Service; m_Service = nullptr; - } - } + Transports::~Transports() { + Stop(); + if (m_Service) { + delete m_PeerCleanupTimer; + m_PeerCleanupTimer = nullptr; + delete m_PeerTestTimer; + m_PeerTestTimer = nullptr; + delete m_Work; + m_Work = nullptr; + delete m_Service; + m_Service = nullptr; + } + } - void Transports::Start (bool enableNTCP2, bool enableSSU, bool enableSSU2) - { - if (!m_Service) - { - m_Service = new boost::asio::io_service (); - m_Work = new boost::asio::io_service::work (*m_Service); - m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service); - m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); - } + void Transports::Start(bool enableNTCP2, bool enableSSU, bool enableSSU2) { + if (!m_Service) { + m_Service = new boost::asio::io_service(); + m_Work = new boost::asio::io_service::work(*m_Service); + m_PeerCleanupTimer = new boost::asio::deadline_timer(*m_Service); + m_PeerTestTimer = new boost::asio::deadline_timer(*m_Service); + } - i2p::config::GetOption("nat", m_IsNAT); - m_X25519KeysPairSupplier.Start (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Transports::Run, this)); - std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); - i2p::http::URL proxyurl; - // create NTCP2. TODO: move to acceptor - if (enableNTCP2 || i2p::context.SupportsMesh ()) - { - if(!ntcp2proxy.empty() && enableNTCP2) - { - if(proxyurl.parse(ntcp2proxy)) - { - if(proxyurl.schema == "socks" || proxyurl.schema == "http") - { - m_NTCP2Server = new NTCP2Server (); - NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy; + i2p::config::GetOption("nat", m_IsNAT); + m_X25519KeysPairSupplier.Start(); + m_IsRunning = true; + m_Thread = new std::thread(std::bind(&Transports::Run, this)); + std::string ntcp2proxy; + i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); + i2p::http::URL proxyurl; + // create NTCP2. TODO: move to acceptor + if (enableNTCP2 || i2p::context.SupportsMesh()) { + if (!ntcp2proxy.empty() && enableNTCP2) { + if (proxyurl.parse(ntcp2proxy)) { + if (proxyurl.schema == "socks" || proxyurl.schema == "http") { + m_NTCP2Server = new NTCP2Server(); + NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy; - if (proxyurl.schema == "http") - proxytype = NTCP2Server::eHTTPProxy; + if (proxyurl.schema == "http") + proxytype = NTCP2Server::eHTTPProxy; - m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass); - i2p::context.SetStatus (eRouterStatusProxy); - } - else - LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy); - } - else - LogPrint(eLogError, "Transports: Invalid NTCP2 proxy URL ", ntcp2proxy); - } - else - m_NTCP2Server = new NTCP2Server (); - } + m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, + proxyurl.pass); + i2p::context.SetStatus(eRouterStatusProxy); + } else + LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy); + } else + LogPrint(eLogError, "Transports: Invalid NTCP2 proxy URL ", ntcp2proxy); + } else + m_NTCP2Server = new NTCP2Server(); + } - // create SSU server - int ssuPort = 0; - if (enableSSU) - { - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) - { - if (!address) continue; - if (address->transportStyle == RouterInfo::eTransportSSU) - { - ssuPort = address->port; - m_SSUServer = new SSUServer (address->port); - break; - } - } - } - // create SSU2 server - if (enableSSU2) m_SSU2Server = new SSU2Server (); + // create SSU server + int ssuPort = 0; + if (enableSSU) { + auto &addresses = context.GetRouterInfo().GetAddresses(); + for (const auto &address: addresses) { + if (!address) continue; + if (address->transportStyle == RouterInfo::eTransportSSU) { + ssuPort = address->port; + m_SSUServer = new SSUServer(address->port); + break; + } + } + } + // create SSU2 server + if (enableSSU2) m_SSU2Server = new SSU2Server(); - // bind to interfaces - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - if (ipv4) - { - std::string address; i2p::config::GetOption("address4", address); - if (!address.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string (address, ec); - if (!ec) - { - if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); - if (m_SSUServer) m_SSUServer->SetLocalAddress (addr); - if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); - } - } - } + // bind to interfaces + bool ipv4; + i2p::config::GetOption("ipv4", ipv4); + if (ipv4) { + std::string address; + i2p::config::GetOption("address4", address); + if (!address.empty()) { + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string(address, ec); + if (!ec) { + if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress(addr); + if (m_SSUServer) m_SSUServer->SetLocalAddress(addr); + if (m_SSU2Server) m_SSU2Server->SetLocalAddress(addr); + } + } + } - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - if (ipv6) - { - std::string address; i2p::config::GetOption("address6", address); - if (!address.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string (address, ec); - if (!ec) - { - if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); - if (m_SSUServer) m_SSUServer->SetLocalAddress (addr); - if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); - } - } - } + bool ipv6; + i2p::config::GetOption("ipv6", ipv6); + if (ipv6) { + std::string address; + i2p::config::GetOption("address6", address); + if (!address.empty()) { + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string(address, ec); + if (!ec) { + if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress(addr); + if (m_SSUServer) m_SSUServer->SetLocalAddress(addr); + if (m_SSU2Server) m_SSU2Server->SetLocalAddress(addr); + } + } + } - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); - if (ygg) - { - std::string address; i2p::config::GetOption("meshnets.yggaddress", address); - if (!address.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string (address, ec); - if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr)) - m_NTCP2Server->SetLocalAddress (addr); - } - } + bool ygg; + i2p::config::GetOption("meshnets.yggdrasil", ygg); + if (ygg) { + std::string address; + i2p::config::GetOption("meshnets.yggaddress", address); + if (!address.empty()) { + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string(address, ec); + if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress(addr)) + m_NTCP2Server->SetLocalAddress(addr); + } + } - // start servers - if (m_NTCP2Server) m_NTCP2Server->Start (); - if (m_SSU2Server) m_SSU2Server->Start (); - if (m_SSUServer) - { - LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort); - try - { - m_SSUServer->Start (); - } - catch (std::exception& ex ) - { - LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort); - m_SSUServer->Stop (); - delete m_SSUServer; - m_SSUServer = nullptr; - } - } - if (m_SSUServer || m_SSU2Server) DetectExternalIP (); + // start servers + if (m_NTCP2Server) m_NTCP2Server->Start(); + if (m_SSU2Server) m_SSU2Server->Start(); + if (m_SSUServer) { + LogPrint(eLogInfo, "Transports: Start listening UDP port ", ssuPort); + try { + m_SSUServer->Start(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort); + m_SSUServer->Stop(); + delete m_SSUServer; + m_SSUServer = nullptr; + } + } + if (m_SSUServer || m_SSU2Server) DetectExternalIP(); - m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + m_PeerCleanupTimer->expires_from_now(boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer->async_wait(std::bind(&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - if (m_IsNAT) - { - m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); - m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); - } - } + if (m_IsNAT) { + m_PeerTestTimer->expires_from_now(boost::posix_time::minutes(PEER_TEST_INTERVAL)); + m_PeerTestTimer->async_wait(std::bind(&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); + } + } - void Transports::Stop () - { - if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel (); - if (m_PeerTestTimer) m_PeerTestTimer->cancel (); - m_Peers.clear (); - if (m_SSUServer) - { - m_SSUServer->Stop (); - delete m_SSUServer; - m_SSUServer = nullptr; - } + void Transports::Stop() { + if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel(); + if (m_PeerTestTimer) m_PeerTestTimer->cancel(); + m_Peers.clear(); + if (m_SSUServer) { + m_SSUServer->Stop(); + delete m_SSUServer; + m_SSUServer = nullptr; + } - if (m_SSU2Server) - { - m_SSU2Server->Stop (); - delete m_SSU2Server; - m_SSU2Server = nullptr; - } + if (m_SSU2Server) { + m_SSU2Server->Stop(); + delete m_SSU2Server; + m_SSU2Server = nullptr; + } - if (m_NTCP2Server) - { - m_NTCP2Server->Stop (); - delete m_NTCP2Server; - m_NTCP2Server = nullptr; - } + if (m_NTCP2Server) { + m_NTCP2Server->Stop(); + delete m_NTCP2Server; + m_NTCP2Server = nullptr; + } - m_X25519KeysPairSupplier.Stop (); - m_IsRunning = false; - if (m_Service) m_Service->stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } + m_X25519KeysPairSupplier.Stop(); + m_IsRunning = false; + if (m_Service) m_Service->stop(); + if (m_Thread) { + m_Thread->join(); + delete m_Thread; + m_Thread = nullptr; + } + } - void Transports::Run () - { - i2p::util::SetThreadName("Transports"); + void Transports::Run() { + i2p::util::SetThreadName("Transports"); - while (m_IsRunning && m_Service) - { - try - { - m_Service->run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Transports: Runtime exception: ", ex.what ()); - } - } - } + while (m_IsRunning && m_Service) { + try { + m_Service->run(); + } + catch (std::exception &ex) { + LogPrint(eLogError, "Transports: Runtime exception: ", ex.what()); + } + } + } - void Transports::UpdateBandwidth () - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - if (m_LastBandwidthUpdateTime > 0) - { - auto delta = ts - m_LastBandwidthUpdateTime; - if (delta > 0) - { - m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second - m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second - m_TransitBandwidth = (m_TotalTransitTransmittedBytes - m_LastTransitBandwidthUpdateBytes)*1000/delta; - } - } - m_LastBandwidthUpdateTime = ts; - m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes; - m_LastOutBandwidthUpdateBytes = m_TotalSentBytes; - m_LastTransitBandwidthUpdateBytes = m_TotalTransitTransmittedBytes; - } + void Transports::UpdateBandwidth() { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + if (m_LastBandwidthUpdateTime > 0) { + auto delta = ts - m_LastBandwidthUpdateTime; + if (delta > 0) { + m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes) * 1000 / delta; // per second + m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes) * 1000 / delta; // per second + m_TransitBandwidth = + (m_TotalTransitTransmittedBytes - m_LastTransitBandwidthUpdateBytes) * 1000 / delta; + } + } + m_LastBandwidthUpdateTime = ts; + m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes; + m_LastOutBandwidthUpdateBytes = m_TotalSentBytes; + m_LastTransitBandwidthUpdateBytes = m_TotalTransitTransmittedBytes; + } - bool Transports::IsBandwidthExceeded () const - { - auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes - auto bw = std::max (m_InBandwidth, m_OutBandwidth); - return bw > limit; - } + bool Transports::IsBandwidthExceeded() const { + auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes + auto bw = std::max(m_InBandwidth, m_OutBandwidth); + return bw > limit; + } - bool Transports::IsTransitBandwidthExceeded () const - { - auto limit = i2p::context.GetTransitBandwidthLimit() * 1024; // convert to bytes - return m_TransitBandwidth > limit; - } + bool Transports::IsTransitBandwidthExceeded() const { + auto limit = i2p::context.GetTransitBandwidthLimit() * 1024; // convert to bytes + return m_TransitBandwidth > limit; + } - void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) - { - if (m_IsOnline) - SendMessages (ident, std::vector > {msg }); - } + void Transports::SendMessage(const i2p::data::IdentHash &ident, std::shared_ptr msg) { + if (m_IsOnline) + SendMessages(ident, std::vector >{msg}); + } - void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs) - { - m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs)); - } + void Transports::SendMessages(const i2p::data::IdentHash &ident, + const std::vector > &msgs) { + m_Service->post(std::bind(&Transports::PostMessages, this, ident, msgs)); + } - void Transports::PostMessages (i2p::data::IdentHash ident, std::vector > msgs) - { - if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) - { - // we send it to ourself - for (auto& it: msgs) - m_LoopbackHandler.PutNextMessage (std::move (it)); - m_LoopbackHandler.Flush (); - return; - } - if(RoutesRestricted() && !IsRestrictedPeer(ident)) return; - auto it = m_Peers.find (ident); - if (it == m_Peers.end ()) - { - bool connected = false; - try - { - auto r = netdb.FindRouter (ident); - if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - std::unique_lock l(m_PeersMutex); - it = m_Peers.insert (std::pair(ident, { 0, r, {}, - ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} })).first; - } - connected = ConnectToPeer (ident, it->second); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ()); - } - if (!connected) return; - } - if (!it->second.sessions.empty ()) - it->second.sessions.front ()->SendI2NPMessages (msgs); - else - { - if (it->second.delayedMessages.size () < MAX_NUM_DELAYED_MESSAGES) - { - for (auto& it1: msgs) - it->second.delayedMessages.push_back (it1); - } - else - { - LogPrint (eLogWarning, "Transports: Delayed messages queue size to ", - ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES); - std::unique_lock l(m_PeersMutex); - m_Peers.erase (it); - } - } - } + void + Transports::PostMessages(i2p::data::IdentHash ident, std::vector > msgs) { + if (ident == i2p::context.GetRouterInfo().GetIdentHash()) { + // we send it to ourself + for (auto &it: msgs) + m_LoopbackHandler.PutNextMessage(std::move(it)); + m_LoopbackHandler.Flush(); + return; + } + if (RoutesRestricted() && !IsRestrictedPeer(ident)) return; + auto it = m_Peers.find(ident); + if (it == m_Peers.end()) { + bool connected = false; + try { + auto r = netdb.FindRouter(ident); + if (r && (r->IsUnreachable() || !r->IsReachableFrom(i2p::context.GetRouterInfo()))) + return; // router found but non-reachable + { + auto ts = i2p::util::GetSecondsSinceEpoch(); + std::unique_lock l(m_PeersMutex); + it = m_Peers.insert(std::pair(ident, {0, r, {}, + ts, ts + + PEER_ROUTER_INFO_UPDATE_INTERVAL, + {}})).first; + } + connected = ConnectToPeer(ident, it->second); + } + catch (std::exception &ex) { + LogPrint(eLogError, "Transports: PostMessages exception:", ex.what()); + } + if (!connected) return; + } + if (!it->second.sessions.empty()) + it->second.sessions.front()->SendI2NPMessages(msgs); + else { + if (it->second.delayedMessages.size() < MAX_NUM_DELAYED_MESSAGES) { + for (auto &it1: msgs) + it->second.delayedMessages.push_back(it1); + } else { + LogPrint(eLogWarning, "Transports: Delayed messages queue size to ", + ident.ToBase64(), " exceeds ", MAX_NUM_DELAYED_MESSAGES); + std::unique_lock l(m_PeersMutex); + m_Peers.erase(it); + } + } + } - bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer) - { - if (!peer.router) // reconnect - peer.router = netdb.FindRouter (ident); // try to get new one from netdb - if (peer.router) // we have RI already - { - if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1 - ipv4 - { - if (m_NTCP2Server) // we support NTCP2 - { - std::shared_ptr address; - if (!peer.numAttempts) // NTCP2 ipv6 - { - if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6)) - { - address = peer.router->GetPublishedNTCP2V6Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - } - peer.numAttempts++; - } - if (!address && peer.numAttempts == 1) // NTCP2 ipv4 - { - if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4)) - { - address = peer.router->GetPublishedNTCP2V4Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - } - peer.numAttempts++; - } - if (address) - { - auto s = std::make_shared (*m_NTCP2Server, peer.router, address); - if( m_NTCP2Server->UsingProxy()) - m_NTCP2Server->ConnectWithProxy(s); - else - m_NTCP2Server->Connect (s); - return true; - } - } - else - peer.numAttempts = 2; // switch to SSU - } - if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU2, 2 - ipv6, 3 - ipv4 - { - if (m_SSU2Server) - { - std::shared_ptr address; - if (peer.numAttempts == 2) // SSU2 ipv6 - { - if (context.GetRouterInfo ().IsSSU2V6 () && peer.router->IsReachableBy (RouterInfo::eSSU2V6)) - { - address = peer.router->GetSSU2V6Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - } - peer.numAttempts++; - } - if (!address && peer.numAttempts == 3) // SSU2 ipv4 - { - if (context.GetRouterInfo ().IsSSU2V4 () && peer.router->IsReachableBy (RouterInfo::eSSU2V4)) - { - address = peer.router->GetSSU2V4Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - } - peer.numAttempts++; - } - if (address && address->IsReachableSSU ()) - { - if (m_SSU2Server->CreateSession (peer.router, address)) - return true; - } - } - else - peer.numAttempts += 2; // switch to mesh - } - if (peer.numAttempts == 4) // Mesh - { - peer.numAttempts++; - if (m_NTCP2Server && context.GetRouterInfo ().IsMesh () && peer.router->IsMesh ()) - { - auto address = peer.router->GetYggdrasilAddress (); - if (address) - { - auto s = std::make_shared (*m_NTCP2Server, peer.router, address); - m_NTCP2Server->Connect (s); - return true; - } - } - } - if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU, 5 - ipv6, 6 - ipv4 - { - if (m_SSUServer) - { - std::shared_ptr address; - if (peer.numAttempts == 5) // SSU ipv6 - { - if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6)) - { - address = peer.router->GetSSUV6Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - } - peer.numAttempts++; - } - if (!address && peer.numAttempts == 6) // SSU ipv4 - { - if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4)) - { - address = peer.router->GetSSUAddress (true); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - } - peer.numAttempts++; - } - if (address && address->IsReachableSSU ()) - { - if (m_SSUServer->CreateSession (peer.router, address)) - return true; - } - } - else - peer.numAttempts += 2; - } - LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available"); - i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed - peer.Done (); - std::unique_lock l(m_PeersMutex); - m_Peers.erase (ident); - return false; - } - else // otherwise request RI - { - LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested"); - i2p::data::netdb.RequestDestination (ident, std::bind ( - &Transports::RequestComplete, this, std::placeholders::_1, ident)); - } - return true; - } + bool Transports::ConnectToPeer(const i2p::data::IdentHash &ident, Peer &peer) { + if (!peer.router) // reconnect + peer.router = netdb.FindRouter(ident); // try to get new one from netdb + if (peer.router) // we have RI already + { + if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1 - ipv4 + { + if (m_NTCP2Server) // we support NTCP2 + { + std::shared_ptr address; + if (!peer.numAttempts) // NTCP2 ipv6 + { + if (context.GetRouterInfo().IsNTCP2V6() && + peer.router->IsReachableBy(RouterInfo::eNTCP2V6)) { + address = peer.router->GetPublishedNTCP2V6Address(); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (!address && peer.numAttempts == 1) // NTCP2 ipv4 + { + if (context.GetRouterInfo().IsNTCP2(true) && + peer.router->IsReachableBy(RouterInfo::eNTCP2V4)) { + address = peer.router->GetPublishedNTCP2V4Address(); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (address) { + auto s = std::make_shared(*m_NTCP2Server, peer.router, address); + if (m_NTCP2Server->UsingProxy()) + m_NTCP2Server->ConnectWithProxy(s); + else + m_NTCP2Server->Connect(s); + return true; + } + } else + peer.numAttempts = 2; // switch to SSU + } + if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU2, 2 - ipv6, 3 - ipv4 + { + if (m_SSU2Server) { + std::shared_ptr address; + if (peer.numAttempts == 2) // SSU2 ipv6 + { + if (context.GetRouterInfo().IsSSU2V6() && peer.router->IsReachableBy(RouterInfo::eSSU2V6)) { + address = peer.router->GetSSU2V6Address(); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (!address && peer.numAttempts == 3) // SSU2 ipv4 + { + if (context.GetRouterInfo().IsSSU2V4() && peer.router->IsReachableBy(RouterInfo::eSSU2V4)) { + address = peer.router->GetSSU2V4Address(); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (address && address->IsReachableSSU()) { + if (m_SSU2Server->CreateSession(peer.router, address)) + return true; + } + } else + peer.numAttempts += 2; // switch to mesh + } + if (peer.numAttempts == 4) // Mesh + { + peer.numAttempts++; + if (m_NTCP2Server && context.GetRouterInfo().IsMesh() && peer.router->IsMesh()) { + auto address = peer.router->GetYggdrasilAddress(); + if (address) { + auto s = std::make_shared(*m_NTCP2Server, peer.router, address); + m_NTCP2Server->Connect(s); + return true; + } + } + } + if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU, 5 - ipv6, 6 - ipv4 + { + if (m_SSUServer) { + std::shared_ptr address; + if (peer.numAttempts == 5) // SSU ipv6 + { + if (context.GetRouterInfo().IsSSUV6() && peer.router->IsReachableBy(RouterInfo::eSSUV6)) { + address = peer.router->GetSSUV6Address(); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (!address && peer.numAttempts == 6) // SSU ipv4 + { + if (context.GetRouterInfo().IsSSU(true) && peer.router->IsReachableBy(RouterInfo::eSSUV4)) { + address = peer.router->GetSSUAddress(true); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (address && address->IsReachableSSU()) { + if (m_SSUServer->CreateSession(peer.router, address)) + return true; + } + } else + peer.numAttempts += 2; + } + LogPrint(eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available"); + i2p::data::netdb.SetUnreachable(ident, true); // we are here because all connection attempts failed + peer.Done(); + std::unique_lock l(m_PeersMutex); + m_Peers.erase(ident); + return false; + } else // otherwise request RI + { + LogPrint(eLogInfo, "Transports: RouterInfo for ", ident.ToBase64(), " not found, requested"); + i2p::data::netdb.RequestDestination(ident, std::bind( + &Transports::RequestComplete, this, std::placeholders::_1, ident)); + } + return true; + } - void Transports::RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) - { - m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); - } + void + Transports::RequestComplete(std::shared_ptr r, const i2p::data::IdentHash &ident) { + m_Service->post(std::bind(&Transports::HandleRequestComplete, this, r, ident)); + } - void Transports::HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident) - { - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - if (r) - { - LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); - it->second.router = r; - ConnectToPeer (ident, it->second); - } - else - { - LogPrint (eLogWarning, "Transports: RouterInfo not found, failed to send messages"); - std::unique_lock l(m_PeersMutex); - m_Peers.erase (it); - } - } - } + void + Transports::HandleRequestComplete(std::shared_ptr r, i2p::data::IdentHash ident) { + auto it = m_Peers.find(ident); + if (it != m_Peers.end()) { + if (r) { + LogPrint(eLogDebug, "Transports: RouterInfo for ", ident.ToBase64(), " found, trying to connect"); + it->second.router = r; + ConnectToPeer(ident, it->second); + } else { + LogPrint(eLogWarning, "Transports: RouterInfo not found, failed to send messages"); + std::unique_lock l(m_PeersMutex); + m_Peers.erase(it); + } + } + } - void Transports::DetectExternalIP () - { - if (RoutesRestricted()) - { - LogPrint(eLogInfo, "Transports: Restricted routes enabled, not detecting IP"); - i2p::context.SetStatus (eRouterStatusOK); - return; - } - if (m_SSUServer || m_SSU2Server) - PeerTest (); - else - LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available"); - } + void Transports::DetectExternalIP() { + if (RoutesRestricted()) { + LogPrint(eLogInfo, "Transports: Restricted routes enabled, not detecting IP"); + i2p::context.SetStatus(eRouterStatusOK); + return; + } + if (m_SSUServer || m_SSU2Server) + PeerTest(); + else + LogPrint(eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available"); + } - void Transports::PeerTest (bool ipv4, bool ipv6) - { - if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return; - if (ipv4 && i2p::context.SupportsV4 ()) - { - LogPrint (eLogInfo, "Transports: Started peer test IPv4"); - std::set excluded; - excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router - if (m_SSUServer) - { - bool statusChanged = false; - for (int i = 0; i < 5; i++) - { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4 - if (router) - { - auto addr = router->GetSSUAddress (true); // ipv4 - if (addr && !i2p::util::net::IsInReservedRange(addr->host)) - { - if (!statusChanged) - { - statusChanged = true; - i2p::context.SetStatus (eRouterStatusTesting); // first time only - } - m_SSUServer->CreateSession (router, addr, true); // peer test v4 - } - excluded.insert (router->GetIdentHash ()); - } - } - if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); - } - // SSU2 - if (m_SSU2Server) - { - excluded.clear (); - excluded.insert (i2p::context.GetIdentHash ()); - for (int i = 0; i < 3; i++) - { - auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 - if (router) - { - if (i2p::context.GetStatus () != eRouterStatusTesting) - i2p::context.SetStatusSSU2 (eRouterStatusTesting); - m_SSU2Server->StartPeerTest (router, true); - excluded.insert (router->GetIdentHash ()); - } - } - } - } - if (ipv6 && i2p::context.SupportsV6 ()) - { - LogPrint (eLogInfo, "Transports: Started peer test IPv6"); - std::set excluded; - excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router - if (m_SSUServer) - { - bool statusChanged = false; - for (int i = 0; i < 5; i++) - { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6 - if (router) - { - auto addr = router->GetSSUV6Address (); - if (addr && !i2p::util::net::IsInReservedRange(addr->host)) - { - if (!statusChanged) - { - statusChanged = true; - i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only - } - m_SSUServer->CreateSession (router, addr, true); // peer test v6 - } - excluded.insert (router->GetIdentHash ()); - } - } - if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); - } + void Transports::PeerTest(bool ipv4, bool ipv6) { + if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return; + if (ipv4 && i2p::context.SupportsV4()) { + LogPrint(eLogInfo, "Transports: Started peer test IPv4"); + std::set excluded; + excluded.insert(i2p::context.GetIdentHash()); // don't pick own router + if (m_SSUServer) { + bool statusChanged = false; + for (int i = 0; i < 5; i++) { + auto router = i2p::data::netdb.GetRandomPeerTestRouter(true, excluded); // v4 + if (router) { + auto addr = router->GetSSUAddress(true); // ipv4 + if (addr && !i2p::util::net::IsInReservedRange(addr->host)) { + if (!statusChanged) { + statusChanged = true; + i2p::context.SetStatus(eRouterStatusTesting); // first time only + } + m_SSUServer->CreateSession(router, addr, true); // peer test v4 + } + excluded.insert(router->GetIdentHash()); + } + } + if (!statusChanged) + LogPrint(eLogWarning, "Transports: Can't find routers for peer test IPv4"); + } + // SSU2 + if (m_SSU2Server) { + excluded.clear(); + excluded.insert(i2p::context.GetIdentHash()); + for (int i = 0; i < 3; i++) { + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter(true, excluded); // v4 + if (router) { + if (i2p::context.GetStatus() != eRouterStatusTesting) + i2p::context.SetStatusSSU2(eRouterStatusTesting); + m_SSU2Server->StartPeerTest(router, true); + excluded.insert(router->GetIdentHash()); + } + } + } + } + if (ipv6 && i2p::context.SupportsV6()) { + LogPrint(eLogInfo, "Transports: Started peer test IPv6"); + std::set excluded; + excluded.insert(i2p::context.GetIdentHash()); // don't pick own router + if (m_SSUServer) { + bool statusChanged = false; + for (int i = 0; i < 5; i++) { + auto router = i2p::data::netdb.GetRandomPeerTestRouter(false, excluded); // v6 + if (router) { + auto addr = router->GetSSUV6Address(); + if (addr && !i2p::util::net::IsInReservedRange(addr->host)) { + if (!statusChanged) { + statusChanged = true; + i2p::context.SetStatusV6(eRouterStatusTesting); // first time only + } + m_SSUServer->CreateSession(router, addr, true); // peer test v6 + } + excluded.insert(router->GetIdentHash()); + } + } + if (!statusChanged) + LogPrint(eLogWarning, "Transports: Can't find routers for peer test IPv6"); + } - // SSU2 - if (m_SSU2Server) - { - excluded.clear (); - excluded.insert (i2p::context.GetIdentHash ()); - for (int i = 0; i < 3; i++) - { - auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 - if (router) - { - if (i2p::context.GetStatusV6 () != eRouterStatusTesting) - i2p::context.SetStatusV6SSU2 (eRouterStatusTesting); - m_SSU2Server->StartPeerTest (router, false); - excluded.insert (router->GetIdentHash ()); - } - } - } - } - } + // SSU2 + if (m_SSU2Server) { + excluded.clear(); + excluded.insert(i2p::context.GetIdentHash()); + for (int i = 0; i < 3; i++) { + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter(false, excluded); // v6 + if (router) { + if (i2p::context.GetStatusV6() != eRouterStatusTesting) + i2p::context.SetStatusV6SSU2(eRouterStatusTesting); + m_SSU2Server->StartPeerTest(router, false); + excluded.insert(router->GetIdentHash()); + } + } + } + } + } - std::shared_ptr Transports::GetNextX25519KeysPair () - { - return m_X25519KeysPairSupplier.Acquire (); - } + std::shared_ptr Transports::GetNextX25519KeysPair() { + return m_X25519KeysPairSupplier.Acquire(); + } - void Transports::ReuseX25519KeysPair (std::shared_ptr pair) - { - m_X25519KeysPairSupplier.Return (pair); - } + void Transports::ReuseX25519KeysPair(std::shared_ptr pair) { + m_X25519KeysPairSupplier.Return(pair); + } - void Transports::PeerConnected (std::shared_ptr session) - { - m_Service->post([session, this]() - { - auto remoteIdentity = session->GetRemoteIdentity (); - if (!remoteIdentity) return; - auto ident = remoteIdentity->GetIdentHash (); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - it->second.router = nullptr; // we don't need RouterInfo after successive connect - bool sendDatabaseStore = true; - if (it->second.delayedMessages.size () > 0) - { - // check if first message is our DatabaseStore (publishing) - auto firstMsg = it->second.delayedMessages[0]; - if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore && - i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ()) - sendDatabaseStore = false; // we have it in the list already - } - if (sendDatabaseStore) - session->SendLocalRouterInfo (); - else - session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds - it->second.sessions.push_back (session); - session->SendI2NPMessages (it->second.delayedMessages); - it->second.delayedMessages.clear (); - } - else // incoming connection - { - if(RoutesRestricted() && ! IsRestrictedPeer(ident)) { - // not trusted - LogPrint(eLogWarning, "Transports: Closing untrusted inbound connection from ", ident.ToBase64()); - session->Done(); - return; - } - session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore - auto ts = i2p::util::GetSecondsSinceEpoch (); - std::unique_lock l(m_PeersMutex); - m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, - ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} })); - } - }); - } + void Transports::PeerConnected(std::shared_ptr session) { + m_Service->post([session, this]() { + auto remoteIdentity = session->GetRemoteIdentity(); + if (!remoteIdentity) return; + auto ident = remoteIdentity->GetIdentHash(); + auto it = m_Peers.find(ident); + if (it != m_Peers.end()) { + it->second.router = nullptr; // we don't need RouterInfo after successive connect + bool sendDatabaseStore = true; + if (it->second.delayedMessages.size() > 0) { + // check if first message is our DatabaseStore (publishing) + auto firstMsg = it->second.delayedMessages[0]; + if (firstMsg && firstMsg->GetTypeID() == eI2NPDatabaseStore && + i2p::data::IdentHash(firstMsg->GetPayload() + DATABASE_STORE_KEY_OFFSET) == + i2p::context.GetIdentHash()) + sendDatabaseStore = false; // we have it in the list already + } + if (sendDatabaseStore) + session->SendLocalRouterInfo(); + else + session->SetTerminationTimeout( + 10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds + it->second.sessions.push_back(session); + session->SendI2NPMessages(it->second.delayedMessages); + it->second.delayedMessages.clear(); + } else // incoming connection + { + if (RoutesRestricted() && !IsRestrictedPeer(ident)) { + // not trusted + LogPrint(eLogWarning, "Transports: Closing untrusted inbound connection from ", + ident.ToBase64()); + session->Done(); + return; + } + session->SendI2NPMessages({CreateDatabaseStoreMsg()}); // send DatabaseStore + auto ts = i2p::util::GetSecondsSinceEpoch(); + std::unique_lock l(m_PeersMutex); + m_Peers.insert(std::make_pair(ident, Peer{0, nullptr, {session}, + ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {}})); + } + }); + } - void Transports::PeerDisconnected (std::shared_ptr session) - { - m_Service->post([session, this]() - { - auto remoteIdentity = session->GetRemoteIdentity (); - if (!remoteIdentity) return; - auto ident = remoteIdentity->GetIdentHash (); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - auto before = it->second.sessions.size (); - it->second.sessions.remove (session); - if (it->second.sessions.empty ()) - { - if (it->second.delayedMessages.size () > 0) - { - if (before > 0) // we had an active session before - it->second.numAttempts = 0; // start over - ConnectToPeer (ident, it->second); - } - else - { - std::unique_lock l(m_PeersMutex); - m_Peers.erase (it); - } - } - } - }); - } + void Transports::PeerDisconnected(std::shared_ptr session) { + m_Service->post([session, this]() { + auto remoteIdentity = session->GetRemoteIdentity(); + if (!remoteIdentity) return; + auto ident = remoteIdentity->GetIdentHash(); + auto it = m_Peers.find(ident); + if (it != m_Peers.end()) { + auto before = it->second.sessions.size(); + it->second.sessions.remove(session); + if (it->second.sessions.empty()) { + if (it->second.delayedMessages.size() > 0) { + if (before > 0) // we had an active session before + it->second.numAttempts = 0; // start over + ConnectToPeer(ident, it->second); + } else { + std::unique_lock l(m_PeersMutex); + m_Peers.erase(it); + } + } + } + }); + } - bool Transports::IsConnected (const i2p::data::IdentHash& ident) const - { - std::unique_lock l(m_PeersMutex); - auto it = m_Peers.find (ident); - return it != m_Peers.end (); - } + bool Transports::IsConnected(const i2p::data::IdentHash &ident) const { + std::unique_lock l(m_PeersMutex); + auto it = m_Peers.find(ident); + return it != m_Peers.end(); + } - void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Peers.begin (); it != m_Peers.end (); ) - { - if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT) - { - LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); - auto profile = i2p::data::GetRouterProfile(it->first); - if (profile) - { - profile->TunnelNonReplied(); - } - std::unique_lock l(m_PeersMutex); - it = m_Peers.erase (it); - } - else - { - if (ts > it->second.nextRouterInfoUpdateTime) - { - auto session = it->second.sessions.front (); - if (session) - session->SendLocalRouterInfo (true); - it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + - rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; - } - ++it; - } - } - UpdateBandwidth (); // TODO: use separate timer(s) for it - bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting; - bool ipv6Testing = i2p::context.GetStatusV6 () == eRouterStatusTesting; - // if still testing, repeat peer test - if (ipv4Testing || ipv6Testing) - PeerTest (ipv4Testing, ipv6Testing); - m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3*SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - } - } + void Transports::HandlePeerCleanupTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto ts = i2p::util::GetSecondsSinceEpoch(); + for (auto it = m_Peers.begin(); it != m_Peers.end();) { + if (it->second.sessions.empty() && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT) { + LogPrint(eLogWarning, "Transports: Session to peer ", it->first.ToBase64(), + " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); + auto profile = i2p::data::GetRouterProfile(it->first); + if (profile) { + profile->TunnelNonReplied(); + } + std::unique_lock l(m_PeersMutex); + it = m_Peers.erase(it); + } else { + if (ts > it->second.nextRouterInfoUpdateTime) { + auto session = it->second.sessions.front(); + if (session) + session->SendLocalRouterInfo(true); + it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + + rand() % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; + } + ++it; + } + } + UpdateBandwidth(); // TODO: use separate timer(s) for it + bool ipv4Testing = i2p::context.GetStatus() == eRouterStatusTesting; + bool ipv6Testing = i2p::context.GetStatusV6() == eRouterStatusTesting; + // if still testing, repeat peer test + if (ipv4Testing || ipv6Testing) + PeerTest(ipv4Testing, ipv6Testing); + m_PeerCleanupTimer->expires_from_now(boost::posix_time::seconds(3 * SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer->async_wait( + std::bind(&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + } + } - void Transports::HandlePeerTestTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - PeerTest (); - m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); - m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); - } - } + void Transports::HandlePeerTestTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + PeerTest(); + m_PeerTestTimer->expires_from_now(boost::posix_time::minutes(PEER_TEST_INTERVAL)); + m_PeerTestTimer->async_wait(std::bind(&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); + } + } - std::shared_ptr Transports::GetRandomPeer () const - { - if (m_Peers.empty ()) return nullptr; - i2p::data::IdentHash ident; - { - std::unique_lock l(m_PeersMutex); - auto it = m_Peers.begin (); - std::advance (it, rand () % m_Peers.size ()); - if (it == m_Peers.end () || it->second.router) return nullptr; // not connected - ident = it->first; - } - return i2p::data::netdb.FindRouter (ident); - } + std::shared_ptr Transports::GetRandomPeer() const { + if (m_Peers.empty()) return nullptr; + i2p::data::IdentHash ident; + { + std::unique_lock l(m_PeersMutex); + auto it = m_Peers.begin(); + std::advance(it, rand() % m_Peers.size()); + if (it == m_Peers.end() || it->second.router) return nullptr; // not connected + ident = it->first; + } + return i2p::data::netdb.FindRouter(ident); + } - void Transports::RestrictRoutesToFamilies(const std::set& families) - { - std::lock_guard lock(m_FamilyMutex); - m_TrustedFamilies.clear(); - for (auto fam : families) - { - boost::to_lower (fam); - auto id = i2p::data::netdb.GetFamilies ().GetFamilyID (fam); - if (id) - m_TrustedFamilies.push_back (id); - } - } + void Transports::RestrictRoutesToFamilies(const std::set &families) { + std::lock_guard lock(m_FamilyMutex); + m_TrustedFamilies.clear(); + for (auto fam: families) { + boost::to_lower(fam); + auto id = i2p::data::netdb.GetFamilies().GetFamilyID(fam); + if (id) + m_TrustedFamilies.push_back(id); + } + } - void Transports::RestrictRoutesToRouters(std::set routers) - { - std::unique_lock lock(m_TrustedRoutersMutex); - m_TrustedRouters.clear(); - for (const auto & ri : routers ) - m_TrustedRouters.push_back(ri); - } + void Transports::RestrictRoutesToRouters(std::set routers) { + std::unique_lock lock(m_TrustedRoutersMutex); + m_TrustedRouters.clear(); + for (const auto &ri: routers) + m_TrustedRouters.push_back(ri); + } - bool Transports::RoutesRestricted() const { - std::unique_lock famlock(m_FamilyMutex); - std::unique_lock routerslock(m_TrustedRoutersMutex); - return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0; - } + bool Transports::RoutesRestricted() const { + std::unique_lock famlock(m_FamilyMutex); + std::unique_lock routerslock(m_TrustedRoutersMutex); + return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0; + } - /** XXX: if routes are not restricted this dies */ - std::shared_ptr Transports::GetRestrictedPeer() const - { - { - std::lock_guard l(m_FamilyMutex); - i2p::data::FamilyID fam = 0; - auto sz = m_TrustedFamilies.size(); - if(sz > 1) - { - auto it = m_TrustedFamilies.begin (); - std::advance(it, rand() % sz); - fam = *it; - } - else if (sz == 1) - { - fam = m_TrustedFamilies[0]; - } - if (fam) - return i2p::data::netdb.GetRandomRouterInFamily(fam); - } - { - std::unique_lock l(m_TrustedRoutersMutex); - auto sz = m_TrustedRouters.size(); - if (sz) - { - if(sz == 1) - return i2p::data::netdb.FindRouter(m_TrustedRouters[0]); - auto it = m_TrustedRouters.begin(); - std::advance(it, rand() % sz); - return i2p::data::netdb.FindRouter(*it); - } - } - return nullptr; - } + /** XXX: if routes are not restricted this dies */ + std::shared_ptr Transports::GetRestrictedPeer() const { + { + std::lock_guard l(m_FamilyMutex); + i2p::data::FamilyID fam = 0; + auto sz = m_TrustedFamilies.size(); + if (sz > 1) { + auto it = m_TrustedFamilies.begin(); + std::advance(it, rand() % sz); + fam = *it; + } else if (sz == 1) { + fam = m_TrustedFamilies[0]; + } + if (fam) + return i2p::data::netdb.GetRandomRouterInFamily(fam); + } + { + std::unique_lock l(m_TrustedRoutersMutex); + auto sz = m_TrustedRouters.size(); + if (sz) { + if (sz == 1) + return i2p::data::netdb.FindRouter(m_TrustedRouters[0]); + auto it = m_TrustedRouters.begin(); + std::advance(it, rand() % sz); + return i2p::data::netdb.FindRouter(*it); + } + } + return nullptr; + } - bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const - { - { - std::unique_lock l(m_TrustedRoutersMutex); - for (const auto & r : m_TrustedRouters ) - if ( r == ih ) return true; - } - { - std::unique_lock l(m_FamilyMutex); - auto ri = i2p::data::netdb.FindRouter(ih); - for (const auto & fam : m_TrustedFamilies) - if(ri->IsFamily(fam)) return true; - } - return false; - } + bool Transports::IsRestrictedPeer(const i2p::data::IdentHash &ih) const { + { + std::unique_lock l(m_TrustedRoutersMutex); + for (const auto &r: m_TrustedRouters) + if (r == ih) return true; + } + { + std::unique_lock l(m_FamilyMutex); + auto ri = i2p::data::netdb.FindRouter(ih); + for (const auto &fam: m_TrustedFamilies) + if (ri->IsFamily(fam)) return true; + } + return false; + } - void Transports::SetOnline (bool online) - { - if (m_IsOnline != online) - { - m_IsOnline = online; - if (online) - PeerTest (); - else - i2p::context.SetError (eRouterErrorOffline); - } - } -} + void Transports::SetOnline(bool online) { + if (m_IsOnline != online) { + m_IsOnline = online; + if (online) + PeerTest(); + else + i2p::context.SetError(eRouterErrorOffline); + } + } + } } diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 78088a8d..cd383af9 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -28,174 +28,215 @@ #include "I2NPProtocol.h" #include "Identity.h" -namespace i2p -{ -namespace transport -{ - template - class EphemeralKeysSupplier - { - // called from this file only, so implementation is in Transports.cpp - public: +namespace i2p { + namespace transport { + template + class EphemeralKeysSupplier { + // called from this file only, so implementation is in Transports.cpp + public: - EphemeralKeysSupplier (int size); - ~EphemeralKeysSupplier (); - void Start (); - void Stop (); - std::shared_ptr Acquire (); - void Return (std::shared_ptr pair); + EphemeralKeysSupplier(int size); - private: + ~EphemeralKeysSupplier(); - void Run (); - void CreateEphemeralKeys (int num); + void Start(); - private: + void Stop(); - const int m_QueueSize; - std::queue > m_Queue; + std::shared_ptr Acquire(); - bool m_IsRunning; - std::thread * m_Thread; - std::condition_variable m_Acquired; - std::mutex m_AcquiredMutex; - }; - typedef EphemeralKeysSupplier X25519KeysPairSupplier; + void Return(std::shared_ptr pair); - const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds - const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds - struct Peer - { - int numAttempts; - std::shared_ptr router; - std::list > sessions; - uint64_t creationTime, nextRouterInfoUpdateTime; - std::vector > delayedMessages; + private: - void Done () - { - for (auto& it: sessions) - it->Done (); - } - }; + void Run(); - const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds - const int PEER_TEST_INTERVAL = 71; // in minutes - const int MAX_NUM_DELAYED_MESSAGES = 150; - class Transports - { - public: + void CreateEphemeralKeys(int num); - Transports (); - ~Transports (); + private: - void Start (bool enableNTCP2=true, bool enableSSU=true, bool enableSSU2=false); - void Stop (); + const int m_QueueSize; + std::queue > m_Queue; - bool IsBoundSSU() const { return m_SSUServer != nullptr; } - bool IsBoundSSU2() const { return m_SSU2Server != nullptr; } - bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } + bool m_IsRunning; + std::thread *m_Thread; + std::condition_variable m_Acquired; + std::mutex m_AcquiredMutex; + }; - bool IsOnline() const { return m_IsOnline; }; - void SetOnline (bool online); + typedef EphemeralKeysSupplier X25519KeysPairSupplier; - boost::asio::io_service& GetService () { return *m_Service; }; - std::shared_ptr GetNextX25519KeysPair (); - void ReuseX25519KeysPair (std::shared_ptr pair); + const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31 * 60; // in seconds + const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7 * 60; // in seconds + struct Peer { + int numAttempts; + std::shared_ptr router; + std::list > sessions; + uint64_t creationTime, nextRouterInfoUpdateTime; + std::vector > delayedMessages; - void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg); - void SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs); + void Done() { + for (auto &it: sessions) + it->Done(); + } + }; - void PeerConnected (std::shared_ptr session); - void PeerDisconnected (std::shared_ptr session); - bool IsConnected (const i2p::data::IdentHash& ident) const; + const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds + const int PEER_TEST_INTERVAL = 71; // in minutes + const int MAX_NUM_DELAYED_MESSAGES = 150; - void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; }; - void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; }; - uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; }; - uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; }; - uint64_t GetTotalTransitTransmittedBytes () const { return m_TotalTransitTransmittedBytes; } - void UpdateTotalTransitTransmittedBytes (uint32_t add) { m_TotalTransitTransmittedBytes += add; }; - uint32_t GetInBandwidth () const { return m_InBandwidth; }; - uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; - uint32_t GetTransitBandwidth () const { return m_TransitBandwidth; }; - bool IsBandwidthExceeded () const; - bool IsTransitBandwidthExceeded () const; - size_t GetNumPeers () const { return m_Peers.size (); }; - std::shared_ptr GetRandomPeer () const; + class Transports { + public: - /** get a trusted first hop for restricted routes */ - std::shared_ptr GetRestrictedPeer() const; - /** do we want to use restricted routes? */ - bool RoutesRestricted() const; - /** restrict routes to use only these router families for first hops */ - void RestrictRoutesToFamilies(const std::set& families); - /** restrict routes to use only these routers for first hops */ - void RestrictRoutesToRouters(std::set routers); + Transports(); - bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const; + ~Transports(); - void PeerTest (bool ipv4 = true, bool ipv6 = true); + void Start(bool enableNTCP2 = true, bool enableSSU = true, bool enableSSU2 = false); - void SetCheckReserved (bool check) { m_CheckReserved = check; }; - bool IsCheckReserved () { return m_CheckReserved; }; + void Stop(); - private: + bool IsBoundSSU() const { return m_SSUServer != nullptr; } - void Run (); - void RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); - void HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident); - void PostMessages (i2p::data::IdentHash ident, std::vector > msgs); - bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer); - void HandlePeerCleanupTimer (const boost::system::error_code& ecode); - void HandlePeerTestTimer (const boost::system::error_code& ecode); + bool IsBoundSSU2() const { return m_SSU2Server != nullptr; } - void UpdateBandwidth (); - void DetectExternalIP (); + bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } - private: + bool IsOnline() const { return m_IsOnline; }; - volatile bool m_IsOnline; - bool m_IsRunning, m_IsNAT, m_CheckReserved; - std::thread * m_Thread; - boost::asio::io_service * m_Service; - boost::asio::io_service::work * m_Work; - boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; + void SetOnline(bool online); - SSUServer * m_SSUServer; - SSU2Server * m_SSU2Server; - NTCP2Server * m_NTCP2Server; - mutable std::mutex m_PeersMutex; - std::unordered_map m_Peers; + boost::asio::io_service &GetService() { return *m_Service; }; - X25519KeysPairSupplier m_X25519KeysPairSupplier; + std::shared_ptr GetNextX25519KeysPair(); - std::atomic m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; - uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; // bytes per second - uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes, m_LastTransitBandwidthUpdateBytes; - uint64_t m_LastBandwidthUpdateTime; + void ReuseX25519KeysPair(std::shared_ptr pair); - /** which router families to trust for first hops */ - std::vector m_TrustedFamilies; - mutable std::mutex m_FamilyMutex; + void SendMessage(const i2p::data::IdentHash &ident, std::shared_ptr msg); - /** which routers for first hop to trust */ - std::vector m_TrustedRouters; - mutable std::mutex m_TrustedRoutersMutex; + void SendMessages(const i2p::data::IdentHash &ident, + const std::vector > &msgs); - i2p::I2NPMessagesHandler m_LoopbackHandler; + void PeerConnected(std::shared_ptr session); - public: + void PeerDisconnected(std::shared_ptr session); - // for HTTP only - const SSUServer * GetSSUServer () const { return m_SSUServer; }; - const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; - const SSU2Server * GetSSU2Server () const { return m_SSU2Server; }; - const decltype(m_Peers)& GetPeers () const { return m_Peers; }; - }; + bool IsConnected(const i2p::data::IdentHash &ident) const; - extern Transports transports; -} + void UpdateSentBytes(uint64_t numBytes) { m_TotalSentBytes += numBytes; }; + + void UpdateReceivedBytes(uint64_t numBytes) { m_TotalReceivedBytes += numBytes; }; + + uint64_t GetTotalSentBytes() const { return m_TotalSentBytes; }; + + uint64_t GetTotalReceivedBytes() const { return m_TotalReceivedBytes; }; + + uint64_t GetTotalTransitTransmittedBytes() const { return m_TotalTransitTransmittedBytes; } + + void UpdateTotalTransitTransmittedBytes(uint32_t add) { m_TotalTransitTransmittedBytes += add; }; + + uint32_t GetInBandwidth() const { return m_InBandwidth; }; + + uint32_t GetOutBandwidth() const { return m_OutBandwidth; }; + + uint32_t GetTransitBandwidth() const { return m_TransitBandwidth; }; + + bool IsBandwidthExceeded() const; + + bool IsTransitBandwidthExceeded() const; + + size_t GetNumPeers() const { return m_Peers.size(); }; + + std::shared_ptr GetRandomPeer() const; + + /** get a trusted first hop for restricted routes */ + std::shared_ptr GetRestrictedPeer() const; + + /** do we want to use restricted routes? */ + bool RoutesRestricted() const; + + /** restrict routes to use only these router families for first hops */ + void RestrictRoutesToFamilies(const std::set &families); + + /** restrict routes to use only these routers for first hops */ + void RestrictRoutesToRouters(std::set routers); + + bool IsRestrictedPeer(const i2p::data::IdentHash &ident) const; + + void PeerTest(bool ipv4 = true, bool ipv6 = true); + + void SetCheckReserved(bool check) { m_CheckReserved = check; }; + + bool IsCheckReserved() { return m_CheckReserved; }; + + private: + + void Run(); + + void RequestComplete(std::shared_ptr r, const i2p::data::IdentHash &ident); + + void HandleRequestComplete(std::shared_ptr r, i2p::data::IdentHash ident); + + void PostMessages(i2p::data::IdentHash ident, std::vector > msgs); + + bool ConnectToPeer(const i2p::data::IdentHash &ident, Peer &peer); + + void HandlePeerCleanupTimer(const boost::system::error_code &ecode); + + void HandlePeerTestTimer(const boost::system::error_code &ecode); + + void UpdateBandwidth(); + + void DetectExternalIP(); + + private: + + volatile bool m_IsOnline; + bool m_IsRunning, m_IsNAT, m_CheckReserved; + std::thread *m_Thread; + boost::asio::io_service *m_Service; + boost::asio::io_service::work *m_Work; + boost::asio::deadline_timer *m_PeerCleanupTimer, *m_PeerTestTimer; + + SSUServer *m_SSUServer; + SSU2Server *m_SSU2Server; + NTCP2Server *m_NTCP2Server; + mutable std::mutex m_PeersMutex; + std::unordered_map m_Peers; + + X25519KeysPairSupplier m_X25519KeysPairSupplier; + + std::atomic m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; + uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; // bytes per second + uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes, m_LastTransitBandwidthUpdateBytes; + uint64_t m_LastBandwidthUpdateTime; + + /** which router families to trust for first hops */ + std::vector m_TrustedFamilies; + mutable std::mutex m_FamilyMutex; + + /** which routers for first hop to trust */ + std::vector m_TrustedRouters; + mutable std::mutex m_TrustedRoutersMutex; + + i2p::I2NPMessagesHandler m_LoopbackHandler; + + public: + + // for HTTP only + const SSUServer *GetSSUServer() const { return m_SSUServer; }; + + const NTCP2Server *GetNTCP2Server() const { return m_NTCP2Server; }; + + const SSU2Server *GetSSU2Server() const { return m_SSU2Server; }; + const decltype(m_Peers) + & + + GetPeers() const { return m_Peers; }; + }; + + extern Transports transports; + } } #endif diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index b578f6c1..fbdb0cf5 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -25,945 +25,825 @@ #include "util.h" #include "ECIESX25519AEADRatchetSession.h" -namespace i2p -{ -namespace tunnel -{ - Tunnel::Tunnel (std::shared_ptr config): - TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), - m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), - m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports), - m_IsRecreated (false), m_Latency (0) - { - } - - Tunnel::~Tunnel () - { - } - - void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) - { - auto numHops = m_Config->GetNumHops (); - const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; - auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); - *msg->GetPayload () = numRecords; - const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; - msg->len += numRecords*recordSize + 1; - // shuffle records - std::vector recordIndicies; - for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()())); - - // create real records - uint8_t * records = msg->GetPayload () + 1; - TunnelHopConfig * hop = m_Config->GetFirstHop (); - int i = 0; - while (hop) - { - uint32_t msgID; - if (hop->next) // we set replyMsgID for last hop only - RAND_bytes ((uint8_t *)&msgID, 4); - else - msgID = replyMsgID; - hop->recordIndex = recordIndicies[i]; i++; - hop->CreateBuildRequestRecord (records, msgID); - hop = hop->next; - } - // fill up fake records with random data - for (int i = numHops; i < numRecords; i++) - { - int idx = recordIndicies[i]; - RAND_bytes (records + idx*recordSize, recordSize); - } - - // decrypt real records - hop = m_Config->GetLastHop ()->prev; - while (hop) - { - // decrypt records after current hop - TunnelHopConfig * hop1 = hop->next; - while (hop1) - { - hop->DecryptRecord (records, hop1->recordIndex); - hop1 = hop1->next; - } - hop = hop->prev; - } - msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); - - // send message - if (outboundTunnel) - { - if (m_Config->IsShort ()) - { - auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr; - if (ident && ident->GetIdentHash () != outboundTunnel->GetNextIdentHash ()) // don't encrypt if IBGW = OBEP - { - auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ()); - if (msg1) msg = msg1; - } - } - outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); - } - else - { - if (m_Config->IsShort () && m_Config->GetLastHop () && - m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent) - { - // add garlic key/tag for reply - uint8_t key[32]; - uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key); - if (m_Pool && m_Pool->GetLocalDestination ()) - m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag); - else - i2p::context.AddECIESx25519Key (key, tag); - } - i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); - } - } - - bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) - { - LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records."); - - TunnelHopConfig * hop = m_Config->GetLastHop (); - while (hop) - { - // decrypt current hop - if (hop->recordIndex >= 0 && hop->recordIndex < msg[0]) - { - if (!hop->DecryptBuildResponseRecord (msg + 1)) - return false; - } - else - { - LogPrint (eLogWarning, "Tunnel: Hop index ", hop->recordIndex, " is out of range"); - return false; - } - - // decrypt records before current hop - TunnelHopConfig * hop1 = hop->prev; - while (hop1) - { - auto idx = hop1->recordIndex; - if (idx >= 0 && idx < msg[0]) - hop->DecryptRecord (msg + 1, idx); - else - LogPrint (eLogWarning, "Tunnel: Hop index ", idx, " is out of range"); - hop1 = hop1->prev; - } - hop = hop->prev; - } - - bool established = true; - size_t numHops = 0; - hop = m_Config->GetFirstHop (); - while (hop) - { - uint8_t ret = hop->GetRetCode (msg + 1); - LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret); - auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); - if (profile) - profile->TunnelBuildResponse (ret); - if (ret) - // if any of participants declined the tunnel is not established - established = false; - hop = hop->next; - numHops++; - } - if (established) - { - // create tunnel decryptions from layer and iv keys in reverse order - m_Hops.resize (numHops); - hop = m_Config->GetLastHop (); - int i = 0; - while (hop) - { - m_Hops[i].ident = hop->ident; - m_Hops[i].decryption.SetKeys (hop->layerKey, hop->ivKey); - hop = hop->prev; - i++; - } - m_IsShortBuildMessage = m_Config->IsShort (); - m_FarEndTransports = m_Config->GetFarEndTransports (); - m_Config = nullptr; - } - if (established) m_State = eTunnelStateEstablished; - return established; - } - - bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const - { - auto latency = GetMeanLatency(); - return latency >= lower && latency <= upper; - } - - void Tunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) - { - const uint8_t * inPayload = in->GetPayload () + 4; - uint8_t * outPayload = out->GetPayload () + 4; - for (auto& it: m_Hops) - { - it.decryption.Decrypt (inPayload, outPayload); - inPayload = outPayload; - } - } - - void Tunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogWarning, "Tunnel: Can't send I2NP messages without delivery instructions"); - } - - std::vector > Tunnel::GetPeers () const - { - auto peers = GetInvertedPeers (); - std::reverse (peers.begin (), peers.end ()); - return peers; - } - - std::vector > Tunnel::GetInvertedPeers () const - { - // hops are in inverted order - std::vector > ret; - for (const auto& it: m_Hops) - ret.push_back (it.ident); - return ret; - } - - void Tunnel::SetState(TunnelState state) - { - m_State = state; - } - - void Tunnel::VisitTunnelHops(TunnelHopVisitor v) - { - // hops are in inverted order, we must return in direct order - for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++) - v((*it).ident); - } - - void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) - { - if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive - EncryptTunnelMsg (msg, msg); - msg->from = shared_from_this (); - m_Endpoint.HandleDecryptedTunnelDataMsg (msg); - } - - ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): - InboundTunnel (std::make_shared ()), - m_NumReceivedBytes (0) - { - } - - void ZeroHopsInboundTunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - if (msg) - { - m_NumReceivedBytes += msg->GetLength (); - msg->from = shared_from_this (); - HandleI2NPMessage (msg); - } - } - - void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) - { - TunnelMessageBlock block; - if (gwHash) - { - block.hash = gwHash; - if (gwTunnel) - { - block.deliveryType = eDeliveryTypeTunnel; - block.tunnelID = gwTunnel; - } - else - block.deliveryType = eDeliveryTypeRouter; - } - else - block.deliveryType = eDeliveryTypeLocal; - block.data = msg; - - SendTunnelDataMsg ({block}); - } - - void OutboundTunnel::SendTunnelDataMsg (const std::vector& msgs) - { - std::unique_lock l(m_SendMutex); - for (auto& it : msgs) - m_Gateway.PutTunnelDataMsg (it); - m_Gateway.SendBuffer (); - } - - void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ()); - } - - ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): - OutboundTunnel (std::make_shared ()), - m_NumSentBytes (0) - { - } - - void ZeroHopsOutboundTunnel::SendTunnelDataMsg (const std::vector& msgs) - { - for (auto& msg : msgs) - { - if (!msg.data) continue; - m_NumSentBytes += msg.data->GetLength (); - switch (msg.deliveryType) - { - case eDeliveryTypeLocal: - HandleI2NPMessage (msg.data); - break; - case eDeliveryTypeTunnel: - i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); - break; - case eDeliveryTypeRouter: - i2p::transport::transports.SendMessage (msg.hash, msg.data); - break; - default: - LogPrint (eLogError, "Tunnel: Unknown delivery type ", (int)msg.deliveryType); - } - } - } - - Tunnels tunnels; - - Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), - m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0) - { - } - - Tunnels::~Tunnels () - { - } - - std::shared_ptr Tunnels::GetTunnel (uint32_t tunnelID) - { - auto it = m_Tunnels.find(tunnelID); - if (it != m_Tunnels.end ()) - return it->second; - return nullptr; - } - - std::shared_ptr Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) - { - return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); - } - - std::shared_ptr Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID) - { - return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels); - } - - template - std::shared_ptr Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels) - { - auto it = pendingTunnels.find(replyMsgID); - if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending) - { - it->second->SetState (eTunnelStateBuildReplyReceived); - return it->second; - } - return nullptr; - } - - std::shared_ptr Tunnels::GetNextInboundTunnel () - { - std::shared_ptr tunnel; - size_t minReceived = 0; - for (const auto& it : m_InboundTunnels) - { - if (!it->IsEstablished ()) continue; - if (!tunnel || it->GetNumReceivedBytes () < minReceived) - { - tunnel = it; - minReceived = it->GetNumReceivedBytes (); - } - } - return tunnel; - } - - std::shared_ptr Tunnels::GetNextOutboundTunnel () - { - if (m_OutboundTunnels.empty ()) return nullptr; - uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0; - std::shared_ptr tunnel; - for (const auto& it: m_OutboundTunnels) - { - if (it->IsEstablished ()) - { - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - return tunnel; - } - - std::shared_ptr Tunnels::CreateTunnelPool (int numInboundHops, int numOutboundHops, - int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance) - { - auto pool = std::make_shared (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance); - std::unique_lock l(m_PoolsMutex); - m_Pools.push_back (pool); - return pool; - } - - void Tunnels::DeleteTunnelPool (std::shared_ptr pool) - { - if (pool) - { - StopTunnelPool (pool); - { - std::unique_lock l(m_PoolsMutex); - m_Pools.remove (pool); - } - } - } - - void Tunnels::StopTunnelPool (std::shared_ptr pool) - { - if (pool) - { - pool->SetActive (false); - pool->DetachTunnels (); - } - } - - void Tunnels::AddTransitTunnel (std::shared_ptr tunnel) - { - if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second) - m_TransitTunnels.push_back (tunnel); - else - LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); - } - - void Tunnels::Start () - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); - } - - void Tunnels::Stop () - { - m_IsRunning = false; - m_Queue.WakeUp (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - } - - void Tunnels::Run () - { - i2p::util::SetThreadName("Tunnels"); - std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready - - uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; - while (m_IsRunning) - { - try - { - auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec - if (msg) - { - uint32_t prevTunnelID = 0, tunnelID = 0; - std::shared_ptr prevTunnel; - do - { - std::shared_ptr tunnel; - uint8_t typeID = msg->GetTypeID (); - switch (typeID) - { - case eI2NPTunnelData: - case eI2NPTunnelGateway: - { - tunnelID = bufbe32toh (msg->GetPayload ()); - if (tunnelID == prevTunnelID) - tunnel = prevTunnel; - else if (prevTunnel) - prevTunnel->FlushTunnelDataMsgs (); - - if (!tunnel) - tunnel = GetTunnel (tunnelID); - if (tunnel) - { - if (typeID == eI2NPTunnelData) - tunnel->HandleTunnelDataMsg (std::move (msg)); - else // tunnel gateway assumed - HandleTunnelGatewayMsg (tunnel, msg); - } - else - LogPrint (eLogWarning, "Tunnel: Tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID); - - break; - } - case eI2NPVariableTunnelBuild: - case eI2NPVariableTunnelBuildReply: - case eI2NPShortTunnelBuild: - case eI2NPShortTunnelBuildReply: - case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); - break; - default: - LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); - } - - msg = m_Queue.Get (); - if (msg) - { - prevTunnelID = tunnelID; - prevTunnel = tunnel; - } - else if (tunnel) - tunnel->FlushTunnelDataMsgs (); - } - while (msg); - } - - if (i2p::transport::transports.IsOnline()) - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastTs >= 15) // manage tunnels every 15 seconds - { - ManageTunnels (); - lastTs = ts; - } - if (ts - lastPoolsTs >= 5) // manage pools every 5 seconds - { - ManageTunnelPools (ts); - lastPoolsTs = ts; - } - if (ts - lastMemoryPoolTs >= 120) // manage memory pool every 2 minutes - { - m_I2NPTunnelEndpointMessagesMemoryPool.CleanUpMt (); - m_I2NPTunnelMessagesMemoryPool.CleanUpMt (); - lastMemoryPoolTs = ts; - } - } - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Tunnel: Runtime exception: ", ex.what ()); - } - } - } - - void Tunnels::HandleTunnelGatewayMsg (std::shared_ptr tunnel, std::shared_ptr msg) - { - if (!tunnel) - { - LogPrint (eLogError, "Tunnel: Missing tunnel for gateway"); - return; - } - const uint8_t * payload = msg->GetPayload (); - uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); - // we make payload as new I2NP message to send - msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; - if (msg->offset + len > msg->len) - { - LogPrint (eLogError, "Tunnel: Gateway payload ", (int)len, " exceeds message length ", (int)msg->len); - return; - } - msg->len = msg->offset + len; - auto typeID = msg->GetTypeID (); - LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); - - if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply) - // transit DatabaseStore my contain new/updated RI - // or DatabaseSearchReply with new routers - i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg)); - tunnel->SendTunnelDataMsg (msg); - } - - void Tunnels::ManageTunnels () - { - ManagePendingTunnels (); - ManageInboundTunnels (); - ManageOutboundTunnels (); - ManageTransitTunnels (); - } - - void Tunnels::ManagePendingTunnels () - { - ManagePendingTunnels (m_PendingInboundTunnels); - ManagePendingTunnels (m_PendingOutboundTunnels); - } - - template - void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels) - { - // check pending tunnel. delete failed or timeout - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) - { - auto tunnel = it->second; - switch (tunnel->GetState ()) - { - case eTunnelStatePending: - if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) - { - LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " timeout, deleted"); - // update stats - auto config = tunnel->GetTunnelConfig (); - if (config) - { - auto hop = config->GetFirstHop (); - while (hop) - { - if (hop->ident) - { - auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); - if (profile) - profile->TunnelNonReplied (); - } - hop = hop->next; - } - } - // delete - it = pendingTunnels.erase (it); - m_NumFailedTunnelCreations++; - } - else - ++it; - break; - case eTunnelStateBuildFailed: - LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); - it = pendingTunnels.erase (it); - m_NumFailedTunnelCreations++; - break; - case eTunnelStateBuildReplyReceived: - // intermediate state, will be either established of build failed - ++it; - break; - default: - // success - it = pendingTunnels.erase (it); - m_NumSuccesiveTunnelCreations++; - } - } - } - - void Tunnels::ManageOutboundTunnels () - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - { - for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) - { - auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->TunnelExpired (tunnel); - // we don't have outbound tunnels in m_Tunnels - it = m_OutboundTunnels.erase (it); - } - else - { - if (tunnel->IsEstablished ()) - { - if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - auto pool = tunnel->GetTunnelPool (); - // let it die if the tunnel pool has been reconfigured and this is old - if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) - { - tunnel->SetRecreated (true); - pool->RecreateOutboundTunnel (tunnel); - } - } - if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - tunnel->SetState (eTunnelStateExpiring); - } - ++it; - } - } - } - - if (m_OutboundTunnels.size () < 3) - { - // trying to create one more oubound tunnel - auto inboundTunnel = GetNextInboundTunnel (); - auto router = i2p::transport::transports.RoutesRestricted() ? - i2p::transport::transports.GetRestrictedPeer() : - i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us - if (!inboundTunnel || !router) return; - LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel"); - CreateTunnel ( - std::make_shared (std::vector > { router->GetRouterIdentity () }, - inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), false), nullptr - ); - } - } - - void Tunnels::ManageInboundTunnels () - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - { - for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) - { - auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->TunnelExpired (tunnel); - m_Tunnels.erase (tunnel->GetTunnelID ()); - it = m_InboundTunnels.erase (it); - } - else - { - if (tunnel->IsEstablished ()) - { - if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - auto pool = tunnel->GetTunnelPool (); - // let it die if the tunnel pool was reconfigured and has different number of hops - if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) - { - tunnel->SetRecreated (true); - pool->RecreateInboundTunnel (tunnel); - } - } - - if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - tunnel->SetState (eTunnelStateExpiring); - else // we don't need to cleanup expiring tunnels - tunnel->Cleanup (); - } - it++; - } - } - } - - if (m_InboundTunnels.empty ()) - { - LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel"); - CreateZeroHopsInboundTunnel (nullptr); - CreateZeroHopsOutboundTunnel (nullptr); - if (!m_ExploratoryPool) - { - int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen); - int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen); - int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum); - int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum); - m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum, 0, 0); - m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ()); - } - return; - } - - if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3) - { - // trying to create one more inbound tunnel - auto router = i2p::transport::transports.RoutesRestricted() ? - i2p::transport::transports.GetRestrictedPeer() : - // should be reachable by us because we send build request directly - i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); - if (!router) { - LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel"); - return; - } - LogPrint (eLogDebug, "Tunnel: Creating one hop inbound tunnel"); - CreateTunnel ( - std::make_shared (std::vector > { router->GetRouterIdentity () }, false), nullptr - ); - } - } - - void Tunnels::ManageTransitTunnels () - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) - { - auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - LogPrint (eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired"); - m_Tunnels.erase (tunnel->GetTunnelID ()); - it = m_TransitTunnels.erase (it); - } - else - { - tunnel->Cleanup (); - it++; - } - } - } - - void Tunnels::ManageTunnelPools (uint64_t ts) - { - std::unique_lock l(m_PoolsMutex); - for (auto& pool : m_Pools) - { - if (pool && pool->IsActive ()) - pool->ManageTunnels (ts); - } - } - - void Tunnels::PostTunnelData (std::shared_ptr msg) - { - if (msg) m_Queue.Put (msg); - } - - void Tunnels::PostTunnelData (const std::vector >& msgs) - { - m_Queue.Put (msgs); - } - - template - std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, - std::shared_ptr pool, std::shared_ptr outboundTunnel) - { - auto newTunnel = std::make_shared (config); - newTunnel->SetTunnelPool (pool); - uint32_t replyMsgID; - RAND_bytes ((uint8_t *)&replyMsgID, 4); - AddPendingTunnel (replyMsgID, newTunnel); - newTunnel->Build (replyMsgID, outboundTunnel); - return newTunnel; - } - - std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, - std::shared_ptr pool, std::shared_ptr outboundTunnel) - { - if (config) - return CreateTunnel(config, pool, outboundTunnel); - else - return CreateZeroHopsInboundTunnel (pool); - } - - std::shared_ptr Tunnels::CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool) - { - if (config) - return CreateTunnel(config, pool); - else - return CreateZeroHopsOutboundTunnel (pool); - } - - void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) - { - m_PendingInboundTunnels[replyMsgID] = tunnel; - } - - void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) - { - m_PendingOutboundTunnels[replyMsgID] = tunnel; - } - - void Tunnels::AddOutboundTunnel (std::shared_ptr newTunnel) - { - // we don't need to insert it to m_Tunnels - m_OutboundTunnels.push_back (newTunnel); - auto pool = newTunnel->GetTunnelPool (); - if (pool && pool->IsActive ()) - pool->TunnelCreated (newTunnel); - else - newTunnel->SetTunnelPool (nullptr); - } - - void Tunnels::AddInboundTunnel (std::shared_ptr newTunnel) - { - if (m_Tunnels.emplace (newTunnel->GetTunnelID (), newTunnel).second) - { - m_InboundTunnels.push_back (newTunnel); - auto pool = newTunnel->GetTunnelPool (); - if (!pool) - { - // build symmetric outbound tunnel - CreateTunnel (std::make_shared(newTunnel->GetInvertedPeers (), - newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash (), false), nullptr, - GetNextOutboundTunnel ()); - } - else - { - if (pool->IsActive ()) - pool->TunnelCreated (newTunnel); - else - newTunnel->SetTunnelPool (nullptr); - } - } - else - LogPrint (eLogError, "Tunnel: Tunnel with id ", newTunnel->GetTunnelID (), " already exists"); - } - - - std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) - { - auto inboundTunnel = std::make_shared (); - inboundTunnel->SetTunnelPool (pool); - inboundTunnel->SetState (eTunnelStateEstablished); - m_InboundTunnels.push_back (inboundTunnel); - m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel; - return inboundTunnel; - } - - std::shared_ptr Tunnels::CreateZeroHopsOutboundTunnel (std::shared_ptr pool) - { - auto outboundTunnel = std::make_shared (); - outboundTunnel->SetTunnelPool (pool); - outboundTunnel->SetState (eTunnelStateEstablished); - m_OutboundTunnels.push_back (outboundTunnel); - // we don't insert into m_Tunnels - return outboundTunnel; - } - - std::shared_ptr Tunnels::NewI2NPTunnelMessage (bool endpoint) - { - if (endpoint) - { - // should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet - auto msg = m_I2NPTunnelEndpointMessagesMemoryPool.AcquireSharedMt (); - msg->Align (6); - msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header - return msg; - } - else - { - auto msg = m_I2NPTunnelMessagesMemoryPool.AcquireSharedMt (); - msg->Align (12); - return msg; - } - } - - int Tunnels::GetTransitTunnelsExpirationTimeout () - { - int timeout = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // TODO: possible race condition with I2PControl - for (const auto& it : m_TransitTunnels) - { - int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; - if (t > timeout) timeout = t; - } - return timeout; - } - - size_t Tunnels::CountTransitTunnels() const - { - // TODO: locking - return m_TransitTunnels.size(); - } - - size_t Tunnels::CountInboundTunnels() const - { - // TODO: locking - return m_InboundTunnels.size(); - } - - size_t Tunnels::CountOutboundTunnels() const - { - // TODO: locking - return m_OutboundTunnels.size(); - } -} +namespace i2p { + namespace tunnel { + Tunnel::Tunnel(std::shared_ptr config) : + TunnelBase(config->GetTunnelID(), config->GetNextTunnelID(), config->GetNextIdentHash()), + m_Config(config), m_IsShortBuildMessage(false), m_Pool(nullptr), + m_State(eTunnelStatePending), m_FarEndTransports(i2p::data::RouterInfo::eAllTransports), + m_IsRecreated(false), m_Latency(0) { + } + + Tunnel::~Tunnel() { + } + + void Tunnel::Build(uint32_t replyMsgID, std::shared_ptr outboundTunnel) { + auto numHops = m_Config->GetNumHops(); + const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; + auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage() : NewI2NPMessage(); + *msg->GetPayload() = numRecords; + const size_t recordSize = m_Config->IsShort() ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + msg->len += numRecords * recordSize + 1; + // shuffle records + std::vector recordIndicies; + for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); + std::shuffle(recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()())); + + // create real records + uint8_t *records = msg->GetPayload() + 1; + TunnelHopConfig *hop = m_Config->GetFirstHop(); + int i = 0; + while (hop) { + uint32_t msgID; + if (hop->next) // we set replyMsgID for last hop only + RAND_bytes((uint8_t * ) & msgID, 4); + else + msgID = replyMsgID; + hop->recordIndex = recordIndicies[i]; + i++; + hop->CreateBuildRequestRecord(records, msgID); + hop = hop->next; + } + // fill up fake records with random data + for (int i = numHops; i < numRecords; i++) { + int idx = recordIndicies[i]; + RAND_bytes(records + idx * recordSize, recordSize); + } + + // decrypt real records + hop = m_Config->GetLastHop()->prev; + while (hop) { + // decrypt records after current hop + TunnelHopConfig *hop1 = hop->next; + while (hop1) { + hop->DecryptRecord(records, hop1->recordIndex); + hop1 = hop1->next; + } + hop = hop->prev; + } + msg->FillI2NPMessageHeader(m_Config->IsShort() ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); + + // send message + if (outboundTunnel) { + if (m_Config->IsShort()) { + auto ident = m_Config->GetFirstHop() ? m_Config->GetFirstHop()->ident : nullptr; + if (ident && + ident->GetIdentHash() != outboundTunnel->GetNextIdentHash()) // don't encrypt if IBGW = OBEP + { + auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter(msg, ident->GetEncryptionPublicKey()); + if (msg1) msg = msg1; + } + } + outboundTunnel->SendTunnelDataMsg(GetNextIdentHash(), 0, msg); + } else { + if (m_Config->IsShort() && m_Config->GetLastHop() && + m_Config->GetLastHop()->ident->GetIdentHash() != m_Config->GetLastHop()->nextIdent) { + // add garlic key/tag for reply + uint8_t key[32]; + uint64_t tag = m_Config->GetLastHop()->GetGarlicKey(key); + if (m_Pool && m_Pool->GetLocalDestination()) + m_Pool->GetLocalDestination()->SubmitECIESx25519Key(key, tag); + else + i2p::context.AddECIESx25519Key(key, tag); + } + i2p::transport::transports.SendMessage(GetNextIdentHash(), msg); + } + } + + bool Tunnel::HandleTunnelBuildResponse(uint8_t *msg, size_t len) { + LogPrint(eLogDebug, "Tunnel: TunnelBuildResponse ", (int) msg[0], " records."); + + TunnelHopConfig *hop = m_Config->GetLastHop(); + while (hop) { + // decrypt current hop + if (hop->recordIndex >= 0 && hop->recordIndex < msg[0]) { + if (!hop->DecryptBuildResponseRecord(msg + 1)) + return false; + } else { + LogPrint(eLogWarning, "Tunnel: Hop index ", hop->recordIndex, " is out of range"); + return false; + } + + // decrypt records before current hop + TunnelHopConfig *hop1 = hop->prev; + while (hop1) { + auto idx = hop1->recordIndex; + if (idx >= 0 && idx < msg[0]) + hop->DecryptRecord(msg + 1, idx); + else + LogPrint(eLogWarning, "Tunnel: Hop index ", idx, " is out of range"); + hop1 = hop1->prev; + } + hop = hop->prev; + } + + bool established = true; + size_t numHops = 0; + hop = m_Config->GetFirstHop(); + while (hop) { + uint8_t ret = hop->GetRetCode(msg + 1); + LogPrint(eLogDebug, "Tunnel: Build response ret code=", (int) ret); + auto profile = i2p::data::netdb.FindRouterProfile(hop->ident->GetIdentHash()); + if (profile) + profile->TunnelBuildResponse(ret); + if (ret) + // if any of participants declined the tunnel is not established + established = false; + hop = hop->next; + numHops++; + } + if (established) { + // create tunnel decryptions from layer and iv keys in reverse order + m_Hops.resize(numHops); + hop = m_Config->GetLastHop(); + int i = 0; + while (hop) { + m_Hops[i].ident = hop->ident; + m_Hops[i].decryption.SetKeys(hop->layerKey, hop->ivKey); + hop = hop->prev; + i++; + } + m_IsShortBuildMessage = m_Config->IsShort(); + m_FarEndTransports = m_Config->GetFarEndTransports(); + m_Config = nullptr; + } + if (established) m_State = eTunnelStateEstablished; + return established; + } + + bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const { + auto latency = GetMeanLatency(); + return latency >= lower && latency <= upper; + } + + void Tunnel::EncryptTunnelMsg(std::shared_ptr in, std::shared_ptr out) { + const uint8_t *inPayload = in->GetPayload() + 4; + uint8_t *outPayload = out->GetPayload() + 4; + for (auto &it: m_Hops) { + it.decryption.Decrypt(inPayload, outPayload); + inPayload = outPayload; + } + } + + void Tunnel::SendTunnelDataMsg(std::shared_ptr msg) { + LogPrint(eLogWarning, "Tunnel: Can't send I2NP messages without delivery instructions"); + } + + std::vector > Tunnel::GetPeers() const { + auto peers = GetInvertedPeers(); + std::reverse(peers.begin(), peers.end()); + return peers; + } + + std::vector > Tunnel::GetInvertedPeers() const { + // hops are in inverted order + std::vector > ret; + for (const auto &it: m_Hops) + ret.push_back(it.ident); + return ret; + } + + void Tunnel::SetState(TunnelState state) { + m_State = state; + } + + void Tunnel::VisitTunnelHops(TunnelHopVisitor v) { + // hops are in inverted order, we must return in direct order + for (auto it = m_Hops.rbegin(); it != m_Hops.rend(); it++) + v((*it).ident); + } + + void InboundTunnel::HandleTunnelDataMsg(std::shared_ptr &&msg) { + if (IsFailed()) SetState(eTunnelStateEstablished); // incoming messages means a tunnel is alive + EncryptTunnelMsg(msg, msg); + msg->from = shared_from_this(); + m_Endpoint.HandleDecryptedTunnelDataMsg(msg); + } + + ZeroHopsInboundTunnel::ZeroHopsInboundTunnel() : + InboundTunnel(std::make_shared()), + m_NumReceivedBytes(0) { + } + + void ZeroHopsInboundTunnel::SendTunnelDataMsg(std::shared_ptr msg) { + if (msg) { + m_NumReceivedBytes += msg->GetLength(); + msg->from = shared_from_this(); + HandleI2NPMessage(msg); + } + } + + void OutboundTunnel::SendTunnelDataMsg(const uint8_t *gwHash, uint32_t gwTunnel, + std::shared_ptr msg) { + TunnelMessageBlock block; + if (gwHash) { + block.hash = gwHash; + if (gwTunnel) { + block.deliveryType = eDeliveryTypeTunnel; + block.tunnelID = gwTunnel; + } else + block.deliveryType = eDeliveryTypeRouter; + } else + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + + SendTunnelDataMsg({block}); + } + + void OutboundTunnel::SendTunnelDataMsg(const std::vector &msgs) { + std::unique_lock l(m_SendMutex); + for (auto &it: msgs) + m_Gateway.PutTunnelDataMsg(it); + m_Gateway.SendBuffer(); + } + + void OutboundTunnel::HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg) { + LogPrint(eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID()); + } + + ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel() : + OutboundTunnel(std::make_shared()), + m_NumSentBytes(0) { + } + + void ZeroHopsOutboundTunnel::SendTunnelDataMsg(const std::vector &msgs) { + for (auto &msg: msgs) { + if (!msg.data) continue; + m_NumSentBytes += msg.data->GetLength(); + switch (msg.deliveryType) { + case eDeliveryTypeLocal: + HandleI2NPMessage(msg.data); + break; + case eDeliveryTypeTunnel: + i2p::transport::transports.SendMessage(msg.hash, + i2p::CreateTunnelGatewayMsg(msg.tunnelID, msg.data)); + break; + case eDeliveryTypeRouter: + i2p::transport::transports.SendMessage(msg.hash, msg.data); + break; + default: + LogPrint(eLogError, "Tunnel: Unknown delivery type ", (int) msg.deliveryType); + } + } + } + + Tunnels tunnels; + + Tunnels::Tunnels() : m_IsRunning(false), m_Thread(nullptr), + m_NumSuccesiveTunnelCreations(0), m_NumFailedTunnelCreations(0) { + } + + Tunnels::~Tunnels() { + } + + std::shared_ptr Tunnels::GetTunnel(uint32_t tunnelID) { + auto it = m_Tunnels.find(tunnelID); + if (it != m_Tunnels.end()) + return it->second; + return nullptr; + } + + std::shared_ptr Tunnels::GetPendingInboundTunnel(uint32_t replyMsgID) { + return GetPendingTunnel(replyMsgID, m_PendingInboundTunnels); + } + + std::shared_ptr Tunnels::GetPendingOutboundTunnel(uint32_t replyMsgID) { + return GetPendingTunnel(replyMsgID, m_PendingOutboundTunnels); + } + + template + std::shared_ptr Tunnels::GetPendingTunnel(uint32_t replyMsgID, + const std::map > &pendingTunnels) { + auto it = pendingTunnels.find(replyMsgID); + if (it != pendingTunnels.end() && it->second->GetState() == eTunnelStatePending) { + it->second->SetState(eTunnelStateBuildReplyReceived); + return it->second; + } + return nullptr; + } + + std::shared_ptr Tunnels::GetNextInboundTunnel() { + std::shared_ptr tunnel; + size_t minReceived = 0; + for (const auto &it: m_InboundTunnels) { + if (!it->IsEstablished()) continue; + if (!tunnel || it->GetNumReceivedBytes() < minReceived) { + tunnel = it; + minReceived = it->GetNumReceivedBytes(); + } + } + return tunnel; + } + + std::shared_ptr Tunnels::GetNextOutboundTunnel() { + if (m_OutboundTunnels.empty()) return nullptr; + uint32_t ind = rand() % m_OutboundTunnels.size(), i = 0; + std::shared_ptr tunnel; + for (const auto &it: m_OutboundTunnels) { + if (it->IsEstablished()) { + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + return tunnel; + } + + std::shared_ptr Tunnels::CreateTunnelPool(int numInboundHops, int numOutboundHops, + int numInboundTunnels, int numOutboundTunnels, + int inboundVariance, int outboundVariance) { + auto pool = std::make_shared(numInboundHops, numOutboundHops, numInboundTunnels, + numOutboundTunnels, inboundVariance, outboundVariance); + std::unique_lock l(m_PoolsMutex); + m_Pools.push_back(pool); + return pool; + } + + void Tunnels::DeleteTunnelPool(std::shared_ptr pool) { + if (pool) { + StopTunnelPool(pool); + { + std::unique_lock l(m_PoolsMutex); + m_Pools.remove(pool); + } + } + } + + void Tunnels::StopTunnelPool(std::shared_ptr pool) { + if (pool) { + pool->SetActive(false); + pool->DetachTunnels(); + } + } + + void Tunnels::AddTransitTunnel(std::shared_ptr tunnel) { + if (m_Tunnels.emplace(tunnel->GetTunnelID(), tunnel).second) + m_TransitTunnels.push_back(tunnel); + else + LogPrint(eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID(), " already exists"); + } + + void Tunnels::Start() { + m_IsRunning = true; + m_Thread = new std::thread(std::bind(&Tunnels::Run, this)); + } + + void Tunnels::Stop() { + m_IsRunning = false; + m_Queue.WakeUp(); + if (m_Thread) { + m_Thread->join(); + delete m_Thread; + m_Thread = 0; + } + } + + void Tunnels::Run() { + i2p::util::SetThreadName("Tunnels"); + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for other parts are ready + + uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; + while (m_IsRunning) { + try { + auto msg = m_Queue.GetNextWithTimeout(1000); // 1 sec + if (msg) { + uint32_t prevTunnelID = 0, tunnelID = 0; + std::shared_ptr prevTunnel; + do { + std::shared_ptr tunnel; + uint8_t typeID = msg->GetTypeID(); + switch (typeID) { + case eI2NPTunnelData: + case eI2NPTunnelGateway: { + tunnelID = bufbe32toh(msg->GetPayload()); + if (tunnelID == prevTunnelID) + tunnel = prevTunnel; + else if (prevTunnel) + prevTunnel->FlushTunnelDataMsgs(); + + if (!tunnel) + tunnel = GetTunnel(tunnelID); + if (tunnel) { + if (typeID == eI2NPTunnelData) + tunnel->HandleTunnelDataMsg(std::move(msg)); + else // tunnel gateway assumed + HandleTunnelGatewayMsg(tunnel, msg); + } else + LogPrint(eLogWarning, "Tunnel: Tunnel not found, tunnelID=", tunnelID, + " previousTunnelID=", prevTunnelID, " type=", (int) typeID); + + break; + } + case eI2NPVariableTunnelBuild: + case eI2NPVariableTunnelBuildReply: + case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuildReply: + case eI2NPTunnelBuild: + case eI2NPTunnelBuildReply: + HandleI2NPMessage(msg->GetBuffer(), msg->GetLength()); + break; + default: + LogPrint(eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); + } + + msg = m_Queue.Get(); + if (msg) { + prevTunnelID = tunnelID; + prevTunnel = tunnel; + } else if (tunnel) + tunnel->FlushTunnelDataMsgs(); + } while (msg); + } + + if (i2p::transport::transports.IsOnline()) { + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + if (ts - lastTs >= 15) // manage tunnels every 15 seconds + { + ManageTunnels(); + lastTs = ts; + } + if (ts - lastPoolsTs >= 5) // manage pools every 5 seconds + { + ManageTunnelPools(ts); + lastPoolsTs = ts; + } + if (ts - lastMemoryPoolTs >= 120) // manage memory pool every 2 minutes + { + m_I2NPTunnelEndpointMessagesMemoryPool.CleanUpMt(); + m_I2NPTunnelMessagesMemoryPool.CleanUpMt(); + lastMemoryPoolTs = ts; + } + } + } + catch (std::exception &ex) { + LogPrint(eLogError, "Tunnel: Runtime exception: ", ex.what()); + } + } + } + + void Tunnels::HandleTunnelGatewayMsg(std::shared_ptr tunnel, std::shared_ptr msg) { + if (!tunnel) { + LogPrint(eLogError, "Tunnel: Missing tunnel for gateway"); + return; + } + const uint8_t *payload = msg->GetPayload(); + uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); + // we make payload as new I2NP message to send + msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; + if (msg->offset + len > msg->len) { + LogPrint(eLogError, "Tunnel: Gateway payload ", (int) len, " exceeds message length ", (int) msg->len); + return; + } + msg->len = msg->offset + len; + auto typeID = msg->GetTypeID(); + LogPrint(eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID(), + ", msg type ", (int) typeID); + + if (IsRouterInfoMsg(msg) || typeID == eI2NPDatabaseSearchReply) + // transit DatabaseStore my contain new/updated RI + // or DatabaseSearchReply with new routers + i2p::data::netdb.PostI2NPMsg(CopyI2NPMessage(msg)); + tunnel->SendTunnelDataMsg(msg); + } + + void Tunnels::ManageTunnels() { + ManagePendingTunnels(); + ManageInboundTunnels(); + ManageOutboundTunnels(); + ManageTransitTunnels(); + } + + void Tunnels::ManagePendingTunnels() { + ManagePendingTunnels(m_PendingInboundTunnels); + ManagePendingTunnels(m_PendingOutboundTunnels); + } + + template + void Tunnels::ManagePendingTunnels(PendingTunnels &pendingTunnels) { + // check pending tunnel. delete failed or timeout + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + for (auto it = pendingTunnels.begin(); it != pendingTunnels.end();) { + auto tunnel = it->second; + switch (tunnel->GetState()) { + case eTunnelStatePending: + if (ts > tunnel->GetCreationTime() + TUNNEL_CREATION_TIMEOUT) { + LogPrint(eLogDebug, "Tunnel: Pending build request ", it->first, " timeout, deleted"); + // update stats + auto config = tunnel->GetTunnelConfig(); + if (config) { + auto hop = config->GetFirstHop(); + while (hop) { + if (hop->ident) { + auto profile = i2p::data::netdb.FindRouterProfile(hop->ident->GetIdentHash()); + if (profile) + profile->TunnelNonReplied(); + } + hop = hop->next; + } + } + // delete + it = pendingTunnels.erase(it); + m_NumFailedTunnelCreations++; + } else + ++it; + break; + case eTunnelStateBuildFailed: + LogPrint(eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); + it = pendingTunnels.erase(it); + m_NumFailedTunnelCreations++; + break; + case eTunnelStateBuildReplyReceived: + // intermediate state, will be either established of build failed + ++it; + break; + default: + // success + it = pendingTunnels.erase(it); + m_NumSuccesiveTunnelCreations++; + } + } + } + + void Tunnels::ManageOutboundTunnels() { + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + { + for (auto it = m_OutboundTunnels.begin(); it != m_OutboundTunnels.end();) { + auto tunnel = *it; + if (ts > tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) { + LogPrint(eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID(), " expired"); + auto pool = tunnel->GetTunnelPool(); + if (pool) + pool->TunnelExpired(tunnel); + // we don't have outbound tunnels in m_Tunnels + it = m_OutboundTunnels.erase(it); + } else { + if (tunnel->IsEstablished()) { + if (!tunnel->IsRecreated() && ts + TUNNEL_RECREATION_THRESHOLD > + tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) { + auto pool = tunnel->GetTunnelPool(); + // let it die if the tunnel pool has been reconfigured and this is old + if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) { + tunnel->SetRecreated(true); + pool->RecreateOutboundTunnel(tunnel); + } + } + if (ts + TUNNEL_EXPIRATION_THRESHOLD > + tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) + tunnel->SetState(eTunnelStateExpiring); + } + ++it; + } + } + } + + if (m_OutboundTunnels.size() < 3) { + // trying to create one more oubound tunnel + auto inboundTunnel = GetNextInboundTunnel(); + auto router = i2p::transport::transports.RoutesRestricted() ? + i2p::transport::transports.GetRestrictedPeer() : + i2p::data::netdb.GetRandomRouter(i2p::context.GetSharedRouterInfo(), + false); // reachable by us + if (!inboundTunnel || !router) return; + LogPrint(eLogDebug, "Tunnel: Creating one hop outbound tunnel"); + CreateTunnel( + std::make_shared( + std::vector >{router->GetRouterIdentity()}, + inboundTunnel->GetNextTunnelID(), inboundTunnel->GetNextIdentHash(), false), nullptr + ); + } + } + + void Tunnels::ManageInboundTunnels() { + uint64_t ts = i2p::util::GetSecondsSinceEpoch(); + { + for (auto it = m_InboundTunnels.begin(); it != m_InboundTunnels.end();) { + auto tunnel = *it; + if (ts > tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) { + LogPrint(eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID(), " expired"); + auto pool = tunnel->GetTunnelPool(); + if (pool) + pool->TunnelExpired(tunnel); + m_Tunnels.erase(tunnel->GetTunnelID()); + it = m_InboundTunnels.erase(it); + } else { + if (tunnel->IsEstablished()) { + if (!tunnel->IsRecreated() && ts + TUNNEL_RECREATION_THRESHOLD > + tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) { + auto pool = tunnel->GetTunnelPool(); + // let it die if the tunnel pool was reconfigured and has different number of hops + if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) { + tunnel->SetRecreated(true); + pool->RecreateInboundTunnel(tunnel); + } + } + + if (ts + TUNNEL_EXPIRATION_THRESHOLD > + tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) + tunnel->SetState(eTunnelStateExpiring); + else // we don't need to cleanup expiring tunnels + tunnel->Cleanup(); + } + it++; + } + } + } + + if (m_InboundTunnels.empty()) { + LogPrint(eLogDebug, "Tunnel: Creating zero hops inbound tunnel"); + CreateZeroHopsInboundTunnel(nullptr); + CreateZeroHopsOutboundTunnel(nullptr); + if (!m_ExploratoryPool) { + int ibLen; + i2p::config::GetOption("exploratory.inbound.length", ibLen); + int obLen; + i2p::config::GetOption("exploratory.outbound.length", obLen); + int ibNum; + i2p::config::GetOption("exploratory.inbound.quantity", ibNum); + int obNum; + i2p::config::GetOption("exploratory.outbound.quantity", obNum); + m_ExploratoryPool = CreateTunnelPool(ibLen, obLen, ibNum, obNum, 0, 0); + m_ExploratoryPool->SetLocalDestination(i2p::context.GetSharedDestination()); + } + return; + } + + if (m_OutboundTunnels.empty() || m_InboundTunnels.size() < 3) { + // trying to create one more inbound tunnel + auto router = i2p::transport::transports.RoutesRestricted() ? + i2p::transport::transports.GetRestrictedPeer() : + // should be reachable by us because we send build request directly + i2p::data::netdb.GetRandomRouter(i2p::context.GetSharedRouterInfo(), false); + if (!router) { + LogPrint(eLogWarning, "Tunnel: Can't find any router, skip creating tunnel"); + return; + } + LogPrint(eLogDebug, "Tunnel: Creating one hop inbound tunnel"); + CreateTunnel( + std::make_shared( + std::vector >{router->GetRouterIdentity()}, + false), nullptr + ); + } + } + + void Tunnels::ManageTransitTunnels() { + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + for (auto it = m_TransitTunnels.begin(); it != m_TransitTunnels.end();) { + auto tunnel = *it; + if (ts > tunnel->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT) { + LogPrint(eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID(), " expired"); + m_Tunnels.erase(tunnel->GetTunnelID()); + it = m_TransitTunnels.erase(it); + } else { + tunnel->Cleanup(); + it++; + } + } + } + + void Tunnels::ManageTunnelPools(uint64_t ts) { + std::unique_lock l(m_PoolsMutex); + for (auto &pool: m_Pools) { + if (pool && pool->IsActive()) + pool->ManageTunnels(ts); + } + } + + void Tunnels::PostTunnelData(std::shared_ptr msg) { + if (msg) m_Queue.Put(msg); + } + + void Tunnels::PostTunnelData(const std::vector > &msgs) { + m_Queue.Put(msgs); + } + + template + std::shared_ptr Tunnels::CreateTunnel(std::shared_ptr config, + std::shared_ptr pool, + std::shared_ptr outboundTunnel) { + auto newTunnel = std::make_shared(config); + newTunnel->SetTunnelPool(pool); + uint32_t replyMsgID; + RAND_bytes((uint8_t * ) & replyMsgID, 4); + AddPendingTunnel(replyMsgID, newTunnel); + newTunnel->Build(replyMsgID, outboundTunnel); + return newTunnel; + } + + std::shared_ptr Tunnels::CreateInboundTunnel(std::shared_ptr config, + std::shared_ptr pool, + std::shared_ptr outboundTunnel) { + if (config) + return CreateTunnel(config, pool, outboundTunnel); + else + return CreateZeroHopsInboundTunnel(pool); + } + + std::shared_ptr + Tunnels::CreateOutboundTunnel(std::shared_ptr config, std::shared_ptr pool) { + if (config) + return CreateTunnel(config, pool); + else + return CreateZeroHopsOutboundTunnel(pool); + } + + void Tunnels::AddPendingTunnel(uint32_t replyMsgID, std::shared_ptr tunnel) { + m_PendingInboundTunnels[replyMsgID] = tunnel; + } + + void Tunnels::AddPendingTunnel(uint32_t replyMsgID, std::shared_ptr tunnel) { + m_PendingOutboundTunnels[replyMsgID] = tunnel; + } + + void Tunnels::AddOutboundTunnel(std::shared_ptr newTunnel) { + // we don't need to insert it to m_Tunnels + m_OutboundTunnels.push_back(newTunnel); + auto pool = newTunnel->GetTunnelPool(); + if (pool && pool->IsActive()) + pool->TunnelCreated(newTunnel); + else + newTunnel->SetTunnelPool(nullptr); + } + + void Tunnels::AddInboundTunnel(std::shared_ptr newTunnel) { + if (m_Tunnels.emplace(newTunnel->GetTunnelID(), newTunnel).second) { + m_InboundTunnels.push_back(newTunnel); + auto pool = newTunnel->GetTunnelPool(); + if (!pool) { + // build symmetric outbound tunnel + CreateTunnel(std::make_shared(newTunnel->GetInvertedPeers(), + newTunnel->GetNextTunnelID(), + newTunnel->GetNextIdentHash(), false), + nullptr, + GetNextOutboundTunnel()); + } else { + if (pool->IsActive()) + pool->TunnelCreated(newTunnel); + else + newTunnel->SetTunnelPool(nullptr); + } + } else + LogPrint(eLogError, "Tunnel: Tunnel with id ", newTunnel->GetTunnelID(), " already exists"); + } + + + std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel(std::shared_ptr pool) { + auto inboundTunnel = std::make_shared(); + inboundTunnel->SetTunnelPool(pool); + inboundTunnel->SetState(eTunnelStateEstablished); + m_InboundTunnels.push_back(inboundTunnel); + m_Tunnels[inboundTunnel->GetTunnelID()] = inboundTunnel; + return inboundTunnel; + } + + std::shared_ptr + Tunnels::CreateZeroHopsOutboundTunnel(std::shared_ptr pool) { + auto outboundTunnel = std::make_shared(); + outboundTunnel->SetTunnelPool(pool); + outboundTunnel->SetState(eTunnelStateEstablished); + m_OutboundTunnels.push_back(outboundTunnel); + // we don't insert into m_Tunnels + return outboundTunnel; + } + + std::shared_ptr Tunnels::NewI2NPTunnelMessage(bool endpoint) { + if (endpoint) { + // should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet + auto msg = m_I2NPTunnelEndpointMessagesMemoryPool.AcquireSharedMt(); + msg->Align(6); + msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header + return msg; + } else { + auto msg = m_I2NPTunnelMessagesMemoryPool.AcquireSharedMt(); + msg->Align(12); + return msg; + } + } + + int Tunnels::GetTransitTunnelsExpirationTimeout() { + int timeout = 0; + uint32_t ts = i2p::util::GetSecondsSinceEpoch(); + // TODO: possible race condition with I2PControl + for (const auto &it: m_TransitTunnels) { + int t = it->GetCreationTime() + TUNNEL_EXPIRATION_TIMEOUT - ts; + if (t > timeout) timeout = t; + } + return timeout; + } + + size_t Tunnels::CountTransitTunnels() const { + // TODO: locking + return m_TransitTunnels.size(); + } + + size_t Tunnels::CountInboundTunnels() const { + // TODO: locking + return m_InboundTunnels.size(); + } + + size_t Tunnels::CountOutboundTunnels() const { + // TODO: locking + return m_OutboundTunnels.size(); + } + } } diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 503b7f9c..e8b6f903 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -29,264 +29,333 @@ #include "TunnelBase.h" #include "I2NPProtocol.h" -namespace i2p -{ -namespace tunnel -{ - const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes - const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute - const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes - const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds - const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message - const int MAX_NUM_RECORDS = 8; - const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds +namespace i2p { + namespace tunnel { + const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes + const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute + const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes + const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds + const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message + const int MAX_NUM_RECORDS = 8; + const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds - const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 - const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6 + const size_t I2NP_TUNNEL_MESSAGE_SIZE = + TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 + const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = + 2 * TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + + 28; // reserved for alignment and NTCP 16 + 6 + 6 - enum TunnelState - { - eTunnelStatePending, - eTunnelStateBuildReplyReceived, - eTunnelStateBuildFailed, - eTunnelStateEstablished, - eTunnelStateTestFailed, - eTunnelStateFailed, - eTunnelStateExpiring - }; + enum TunnelState { + eTunnelStatePending, + eTunnelStateBuildReplyReceived, + eTunnelStateBuildFailed, + eTunnelStateEstablished, + eTunnelStateTestFailed, + eTunnelStateFailed, + eTunnelStateExpiring + }; - class OutboundTunnel; - class InboundTunnel; - class Tunnel: public TunnelBase - { - struct TunnelHop - { - std::shared_ptr ident; - i2p::crypto::TunnelDecryption decryption; - }; + class OutboundTunnel; - public: + class InboundTunnel; - /** function for visiting a hops stored in a tunnel */ - typedef std::function)> TunnelHopVisitor; + class Tunnel : public TunnelBase { + struct TunnelHop { + std::shared_ptr ident; + i2p::crypto::TunnelDecryption decryption; + }; - Tunnel (std::shared_ptr config); - ~Tunnel (); + public: - void Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); + /** function for visiting a hops stored in a tunnel */ + typedef std::function)> TunnelHopVisitor; - std::shared_ptr GetTunnelConfig () const { return m_Config; } - std::vector > GetPeers () const; - std::vector > GetInvertedPeers () const; - bool IsShortBuildMessage () const { return m_IsShortBuildMessage; }; - i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; }; - TunnelState GetState () const { return m_State; }; - void SetState (TunnelState state); - bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; - bool IsFailed () const { return m_State == eTunnelStateFailed; }; - bool IsRecreated () const { return m_IsRecreated; }; - void SetRecreated (bool recreated) { m_IsRecreated = recreated; }; - int GetNumHops () const { return m_Hops.size (); }; - virtual bool IsInbound() const = 0; + Tunnel(std::shared_ptr config); - std::shared_ptr GetTunnelPool () const { return m_Pool; }; - void SetTunnelPool (std::shared_ptr pool) { m_Pool = pool; }; + ~Tunnel(); - bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); + void Build(uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); - // implements TunnelBase - void SendTunnelDataMsg (std::shared_ptr msg); - void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); + std::shared_ptr GetTunnelConfig() const { return m_Config; } - /** @brief add latency sample */ - void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; } - /** @brief get this tunnel's estimated latency */ - uint64_t GetMeanLatency() const { return m_Latency; } - /** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */ - bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; + std::vector > GetPeers() const; - bool LatencyIsKnown() const { return m_Latency > 0; } - bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } + std::vector > GetInvertedPeers() const; - /** visit all hops we currently store */ - void VisitTunnelHops(TunnelHopVisitor v); + bool IsShortBuildMessage() const { return m_IsShortBuildMessage; }; - private: + i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports() const { return m_FarEndTransports; }; - std::shared_ptr m_Config; - std::vector m_Hops; - bool m_IsShortBuildMessage; - std::shared_ptr m_Pool; // pool, tunnel belongs to, or null - TunnelState m_State; - i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; - bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace - uint64_t m_Latency; // in milliseconds - }; + TunnelState GetState() const { return m_State; }; - class OutboundTunnel: public Tunnel - { - public: + void SetState(TunnelState state); - OutboundTunnel (std::shared_ptr config): - Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {}; + bool IsEstablished() const { return m_State == eTunnelStateEstablished; }; - void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); - virtual void SendTunnelDataMsg (const std::vector& msgs); // multiple messages - const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; }; - virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; + bool IsFailed() const { return m_State == eTunnelStateFailed; }; - // implements TunnelBase - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); + bool IsRecreated() const { return m_IsRecreated; }; - bool IsInbound() const { return false; } + void SetRecreated(bool recreated) { m_IsRecreated = recreated; }; - private: + int GetNumHops() const { return m_Hops.size(); }; - std::mutex m_SendMutex; - TunnelGateway m_Gateway; - i2p::data::IdentHash m_EndpointIdentHash; - }; + virtual bool IsInbound() const = 0; - class InboundTunnel: public Tunnel, public std::enable_shared_from_this - { - public: + std::shared_ptr GetTunnelPool() const { return m_Pool; }; - InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; - void HandleTunnelDataMsg (std::shared_ptr&& msg); - virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; - bool IsInbound() const { return true; } + void SetTunnelPool(std::shared_ptr pool) { m_Pool = pool; }; - // override TunnelBase - void Cleanup () { m_Endpoint.Cleanup (); }; + bool HandleTunnelBuildResponse(uint8_t *msg, size_t len); - private: + // implements TunnelBase + void SendTunnelDataMsg(std::shared_ptr msg); - TunnelEndpoint m_Endpoint; - }; + void EncryptTunnelMsg(std::shared_ptr in, std::shared_ptr out); - class ZeroHopsInboundTunnel: public InboundTunnel - { - public: + /** @brief add latency sample */ + void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; } - ZeroHopsInboundTunnel (); - void SendTunnelDataMsg (std::shared_ptr msg); - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + /** @brief get this tunnel's estimated latency */ + uint64_t GetMeanLatency() const { return m_Latency; } - private: + /** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */ + bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; - size_t m_NumReceivedBytes; - }; + bool LatencyIsKnown() const { return m_Latency > 0; } - class ZeroHopsOutboundTunnel: public OutboundTunnel - { - public: + bool IsSlow() const { return LatencyIsKnown() && (int) m_Latency > HIGH_LATENCY_PER_HOP * GetNumHops(); } - ZeroHopsOutboundTunnel (); - void SendTunnelDataMsg (const std::vector& msgs); - size_t GetNumSentBytes () const { return m_NumSentBytes; }; + /** visit all hops we currently store */ + void VisitTunnelHops(TunnelHopVisitor v); - private: + private: - size_t m_NumSentBytes; - }; + std::shared_ptr m_Config; + std::vector m_Hops; + bool m_IsShortBuildMessage; + std::shared_ptr m_Pool; // pool, tunnel belongs to, or null + TunnelState m_State; + i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; + bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace + uint64_t m_Latency; // in milliseconds + }; - class Tunnels - { - public: + class OutboundTunnel : public Tunnel { + public: - Tunnels (); - ~Tunnels (); - void Start (); - void Stop (); + OutboundTunnel(std::shared_ptr config) : + Tunnel(config), m_Gateway(this), m_EndpointIdentHash(config->GetLastIdentHash()) {}; - std::shared_ptr GetPendingInboundTunnel (uint32_t replyMsgID); - std::shared_ptr GetPendingOutboundTunnel (uint32_t replyMsgID); - std::shared_ptr GetNextInboundTunnel (); - std::shared_ptr GetNextOutboundTunnel (); - std::shared_ptr GetExploratoryPool () const { return m_ExploratoryPool; }; - std::shared_ptr GetTunnel (uint32_t tunnelID); - int GetTransitTunnelsExpirationTimeout (); - void AddTransitTunnel (std::shared_ptr tunnel); - void AddOutboundTunnel (std::shared_ptr newTunnel); - void AddInboundTunnel (std::shared_ptr newTunnel); - std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel); - std::shared_ptr CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool); - void PostTunnelData (std::shared_ptr msg); - void PostTunnelData (const std::vector >& msgs); - void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - std::shared_ptr CreateTunnelPool (int numInboundHops, int numOuboundHops, - int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance); - void DeleteTunnelPool (std::shared_ptr pool); - void StopTunnelPool (std::shared_ptr pool); + void SendTunnelDataMsg(const uint8_t *gwHash, uint32_t gwTunnel, std::shared_ptr msg); - std::shared_ptr NewI2NPTunnelMessage (bool endpoint); + virtual void SendTunnelDataMsg(const std::vector &msgs); // multiple messages + const i2p::data::IdentHash &GetEndpointIdentHash() const { return m_EndpointIdentHash; }; - private: + virtual size_t GetNumSentBytes() const { return m_Gateway.GetNumSentBytes(); }; - template - std::shared_ptr CreateTunnel (std::shared_ptr config, - std::shared_ptr pool, std::shared_ptr outboundTunnel = nullptr); + // implements TunnelBase + void HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg); - template - std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); + bool IsInbound() const { return false; } - void HandleTunnelGatewayMsg (std::shared_ptr tunnel, std::shared_ptr msg); + private: - void Run (); - void ManageTunnels (); - void ManageOutboundTunnels (); - void ManageInboundTunnels (); - void ManageTransitTunnels (); - void ManagePendingTunnels (); - template - void ManagePendingTunnels (PendingTunnels& pendingTunnels); - void ManageTunnelPools (uint64_t ts); + std::mutex m_SendMutex; + TunnelGateway m_Gateway; + i2p::data::IdentHash m_EndpointIdentHash; + }; - std::shared_ptr CreateZeroHopsInboundTunnel (std::shared_ptr pool); - std::shared_ptr CreateZeroHopsOutboundTunnel (std::shared_ptr pool); + class InboundTunnel : public Tunnel, public std::enable_shared_from_this { + public: - private: + InboundTunnel(std::shared_ptr config) : Tunnel(config), m_Endpoint(true) {}; - bool m_IsRunning; - std::thread * m_Thread; - std::map > m_PendingInboundTunnels; // by replyMsgID - std::map > m_PendingOutboundTunnels; // by replyMsgID - std::list > m_InboundTunnels; - std::list > m_OutboundTunnels; - std::list > m_TransitTunnels; - std::unordered_map > m_Tunnels; // tunnelID->tunnel known by this id - std::mutex m_PoolsMutex; - std::list> m_Pools; - std::shared_ptr m_ExploratoryPool; - i2p::util::Queue > m_Queue; - i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; - i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; + void HandleTunnelDataMsg(std::shared_ptr &&msg); - // some stats - int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; + virtual size_t GetNumReceivedBytes() const { return m_Endpoint.GetNumReceivedBytes(); }; - public: + bool IsInbound() const { return true; } - // for HTTP only - const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; - const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; }; + // override TunnelBase + void Cleanup() { m_Endpoint.Cleanup(); }; - size_t CountTransitTunnels() const; - size_t CountInboundTunnels() const; - size_t CountOutboundTunnels() const; + private: - int GetQueueSize () { return m_Queue.GetSize (); }; - int GetTunnelCreationSuccessRate () const // in percents - { - int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations; - return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0; - } - }; + TunnelEndpoint m_Endpoint; + }; - extern Tunnels tunnels; -} + class ZeroHopsInboundTunnel : public InboundTunnel { + public: + + ZeroHopsInboundTunnel(); + + void SendTunnelDataMsg(std::shared_ptr msg); + + size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; }; + + private: + + size_t m_NumReceivedBytes; + }; + + class ZeroHopsOutboundTunnel : public OutboundTunnel { + public: + + ZeroHopsOutboundTunnel(); + + void SendTunnelDataMsg(const std::vector &msgs); + + size_t GetNumSentBytes() const { return m_NumSentBytes; }; + + private: + + size_t m_NumSentBytes; + }; + + class Tunnels { + public: + + Tunnels(); + + ~Tunnels(); + + void Start(); + + void Stop(); + + std::shared_ptr GetPendingInboundTunnel(uint32_t replyMsgID); + + std::shared_ptr GetPendingOutboundTunnel(uint32_t replyMsgID); + + std::shared_ptr GetNextInboundTunnel(); + + std::shared_ptr GetNextOutboundTunnel(); + + std::shared_ptr GetExploratoryPool() const { return m_ExploratoryPool; }; + + std::shared_ptr GetTunnel(uint32_t tunnelID); + + int GetTransitTunnelsExpirationTimeout(); + + void AddTransitTunnel(std::shared_ptr tunnel); + + void AddOutboundTunnel(std::shared_ptr newTunnel); + + void AddInboundTunnel(std::shared_ptr newTunnel); + + std::shared_ptr + CreateInboundTunnel(std::shared_ptr config, std::shared_ptr pool, + std::shared_ptr outboundTunnel); + + std::shared_ptr + CreateOutboundTunnel(std::shared_ptr config, std::shared_ptr pool); + + void PostTunnelData(std::shared_ptr msg); + + void PostTunnelData(const std::vector > &msgs); + + void AddPendingTunnel(uint32_t replyMsgID, std::shared_ptr tunnel); + + void AddPendingTunnel(uint32_t replyMsgID, std::shared_ptr tunnel); + + std::shared_ptr CreateTunnelPool(int numInboundHops, int numOuboundHops, + int numInboundTunnels, int numOutboundTunnels, + int inboundVariance, int outboundVariance); + + void DeleteTunnelPool(std::shared_ptr pool); + + void StopTunnelPool(std::shared_ptr pool); + + std::shared_ptr NewI2NPTunnelMessage(bool endpoint); + + private: + + template + std::shared_ptr CreateTunnel(std::shared_ptr config, + std::shared_ptr pool, + std::shared_ptr outboundTunnel = nullptr); + + template + std::shared_ptr + GetPendingTunnel(uint32_t replyMsgID, const std::map > &pendingTunnels); + + void HandleTunnelGatewayMsg(std::shared_ptr tunnel, std::shared_ptr msg); + + void Run(); + + void ManageTunnels(); + + void ManageOutboundTunnels(); + + void ManageInboundTunnels(); + + void ManageTransitTunnels(); + + void ManagePendingTunnels(); + + template + void ManagePendingTunnels(PendingTunnels &pendingTunnels); + + void ManageTunnelPools(uint64_t ts); + + std::shared_ptr CreateZeroHopsInboundTunnel(std::shared_ptr pool); + + std::shared_ptr CreateZeroHopsOutboundTunnel(std::shared_ptr pool); + + private: + + bool m_IsRunning; + std::thread *m_Thread; + std::map > m_PendingInboundTunnels; // by replyMsgID + std::map > m_PendingOutboundTunnels; // by replyMsgID + std::list > m_InboundTunnels; + std::list > m_OutboundTunnels; + std::list > m_TransitTunnels; + std::unordered_map > m_Tunnels; // tunnelID->tunnel known by this id + std::mutex m_PoolsMutex; + std::list> m_Pools; + std::shared_ptr m_ExploratoryPool; + i2p::util::Queue > m_Queue; + i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; + i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; + + // some stats + int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; + + public: + + // for HTTP only + const decltype(m_OutboundTunnels) + & + + GetOutboundTunnels() const { return m_OutboundTunnels; }; + const decltype(m_InboundTunnels) + & + + GetInboundTunnels() const { return m_InboundTunnels; }; + const decltype(m_TransitTunnels) + & + + GetTransitTunnels() const { return m_TransitTunnels; }; + + size_t CountTransitTunnels() const; + + size_t CountInboundTunnels() const; + + size_t CountOutboundTunnels() const; + + int GetQueueSize() { return m_Queue.GetSize(); }; + + int GetTunnelCreationSuccessRate() const // in percents + { + int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations; + return totalNum ? m_NumSuccesiveTunnelCreations * 100 / totalNum : 0; + } + }; + + extern Tunnels tunnels; + } } #endif diff --git a/libi2pd/TunnelBase.h b/libi2pd/TunnelBase.h index 8d0edff1..27ca0288 100644 --- a/libi2pd/TunnelBase.h +++ b/libi2pd/TunnelBase.h @@ -15,68 +15,70 @@ #include "I2NPProtocol.h" #include "Identity.h" -namespace i2p -{ -namespace tunnel -{ - const size_t TUNNEL_DATA_MSG_SIZE = 1028; - const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008; - const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003; +namespace i2p { + namespace tunnel { + const size_t TUNNEL_DATA_MSG_SIZE = 1028; + const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008; + const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003; - enum TunnelDeliveryType - { - eDeliveryTypeLocal = 0, - eDeliveryTypeTunnel = 1, - eDeliveryTypeRouter = 2 - }; - struct TunnelMessageBlock - { - TunnelDeliveryType deliveryType; - i2p::data::IdentHash hash; - uint32_t tunnelID; - std::shared_ptr data; - }; + enum TunnelDeliveryType { + eDeliveryTypeLocal = 0, + eDeliveryTypeTunnel = 1, + eDeliveryTypeRouter = 2 + }; + struct TunnelMessageBlock { + TunnelDeliveryType deliveryType; + i2p::data::IdentHash hash; + uint32_t tunnelID; + std::shared_ptr data; + }; - class TunnelBase - { - public: + class TunnelBase { + public: - TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, i2p::data::IdentHash nextIdent): - m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent), - m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; - virtual ~TunnelBase () {}; - virtual void Cleanup () {}; + TunnelBase(uint32_t tunnelID, uint32_t nextTunnelID, i2p::data::IdentHash nextIdent) : + m_TunnelID(tunnelID), m_NextTunnelID(nextTunnelID), m_NextIdent(nextIdent), + m_CreationTime(i2p::util::GetSecondsSinceEpoch()) {}; - virtual void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) = 0; - virtual void SendTunnelDataMsg (std::shared_ptr msg) = 0; - virtual void FlushTunnelDataMsgs () {}; - virtual void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) = 0; - uint32_t GetNextTunnelID () const { return m_NextTunnelID; }; - const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; }; - virtual uint32_t GetTunnelID () const { return m_TunnelID; }; // as known at our side + virtual ~TunnelBase() {}; - uint32_t GetCreationTime () const { return m_CreationTime; }; - void SetCreationTime (uint32_t t) { m_CreationTime = t; }; + virtual void Cleanup() {}; - private: + virtual void HandleTunnelDataMsg(std::shared_ptr &&tunnelMsg) = 0; - uint32_t m_TunnelID, m_NextTunnelID; - i2p::data::IdentHash m_NextIdent; - uint32_t m_CreationTime; // seconds since epoch - }; + virtual void SendTunnelDataMsg(std::shared_ptr msg) = 0; - struct TunnelCreationTimeCmp - { - template - bool operator() (const std::shared_ptr & t1, const std::shared_ptr & t2) const - { - if (t1->GetCreationTime () != t2->GetCreationTime ()) - return t1->GetCreationTime () > t2->GetCreationTime (); - else - return t1 < t2; - } - }; -} + virtual void FlushTunnelDataMsgs() {}; + + virtual void EncryptTunnelMsg(std::shared_ptr in, std::shared_ptr out) = 0; + + uint32_t GetNextTunnelID() const { return m_NextTunnelID; }; + + const i2p::data::IdentHash &GetNextIdentHash() const { return m_NextIdent; }; + + virtual uint32_t GetTunnelID() const { return m_TunnelID; }; // as known at our side + + uint32_t GetCreationTime() const { return m_CreationTime; }; + + void SetCreationTime(uint32_t t) { m_CreationTime = t; }; + + private: + + uint32_t m_TunnelID, m_NextTunnelID; + i2p::data::IdentHash m_NextIdent; + uint32_t m_CreationTime; // seconds since epoch + }; + + struct TunnelCreationTimeCmp { + template + bool operator()(const std::shared_ptr &t1, const std::shared_ptr &t2) const { + if (t1->GetCreationTime() != t2->GetCreationTime()) + return t1->GetCreationTime() > t2->GetCreationTime(); + else + return t1 < t2; + } + }; + } } #endif diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index e19b515d..b8e38e59 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -17,234 +17,216 @@ #include "I2NPProtocol.h" #include "TunnelConfig.h" -namespace i2p -{ -namespace tunnel -{ - TunnelHopConfig::TunnelHopConfig (std::shared_ptr r) - { - RAND_bytes ((uint8_t *)&tunnelID, 4); - if (!tunnelID) tunnelID = 1; // tunnelID can't be zero - isGateway = true; - isEndpoint = true; - ident = r; - //nextRouter = nullptr; - nextTunnelID = 0; +namespace i2p { + namespace tunnel { + TunnelHopConfig::TunnelHopConfig(std::shared_ptr r) { + RAND_bytes((uint8_t * ) & tunnelID, 4); + if (!tunnelID) tunnelID = 1; // tunnelID can't be zero + isGateway = true; + isEndpoint = true; + ident = r; + //nextRouter = nullptr; + nextTunnelID = 0; - next = nullptr; - prev = nullptr; - } + next = nullptr; + prev = nullptr; + } - void TunnelHopConfig::SetNextIdent (const i2p::data::IdentHash& ident) - { - nextIdent = ident; - isEndpoint = false; - RAND_bytes ((uint8_t *)&nextTunnelID, 4); - if (!nextTunnelID) nextTunnelID = 1; // tunnelID can't be zero - } + void TunnelHopConfig::SetNextIdent(const i2p::data::IdentHash &ident) { + nextIdent = ident; + isEndpoint = false; + RAND_bytes((uint8_t * ) & nextTunnelID, 4); + if (!nextTunnelID) nextTunnelID = 1; // tunnelID can't be zero + } - void TunnelHopConfig::SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) - { - nextIdent = replyIdent; - nextTunnelID = replyTunnelID; - isEndpoint = true; - } + void TunnelHopConfig::SetReplyHop(uint32_t replyTunnelID, const i2p::data::IdentHash &replyIdent) { + nextIdent = replyIdent; + nextTunnelID = replyTunnelID; + isEndpoint = true; + } - void TunnelHopConfig::SetNext (TunnelHopConfig * n) - { - next = n; - if (next) - { - next->prev = this; - next->isGateway = false; - isEndpoint = false; - nextIdent = next->ident->GetIdentHash (); - nextTunnelID = next->tunnelID; - } - } + void TunnelHopConfig::SetNext(TunnelHopConfig *n) { + next = n; + if (next) { + next->prev = this; + next->isGateway = false; + isEndpoint = false; + nextIdent = next->ident->GetIdentHash(); + nextTunnelID = next->tunnelID; + } + } - void TunnelHopConfig::SetPrev (TunnelHopConfig * p) - { - prev = p; - if (prev) - { - prev->next = this; - prev->isEndpoint = false; - isGateway = false; - } - } + void TunnelHopConfig::SetPrev(TunnelHopConfig *p) { + prev = p; + if (prev) { + prev->next = this; + prev->isEndpoint = false; + isGateway = false; + } + } - void TunnelHopConfig::DecryptRecord (uint8_t * records, int index) const - { - uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE; - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (replyKey); - decryption.SetIV (replyIV); - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); - } + void TunnelHopConfig::DecryptRecord(uint8_t *records, int index) const { + uint8_t *record = records + index * TUNNEL_BUILD_RECORD_SIZE; + i2p::crypto::CBCDecryption decryption; + decryption.SetKey(replyKey); + decryption.SetIV(replyIV); + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + } - void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted) - { - if (!ident) return; - i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ()); - auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); - MixHash (encrypted, 32); // h = SHA256(h || sepk) - encrypted += 32; - uint8_t sharedSecret[32]; - ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk) - MixKey (sharedSecret); - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed"); - return; - } - MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext) - } + void ECIESTunnelHopConfig::EncryptECIES(const uint8_t *plainText, size_t len, uint8_t *encrypted) { + if (!ident) return; + i2p::crypto::InitNoiseNState(*this, ident->GetEncryptionPublicKey()); + auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair(); + memcpy(encrypted, ephemeralKeys->GetPublicKey(), 32); + MixHash(encrypted, 32); // h = SHA256(h || sepk) + encrypted += 32; + uint8_t sharedSecret[32]; + ephemeralKeys->Agree(ident->GetEncryptionPublicKey(), sharedSecret); // x25519(sesk, hepk) + MixKey(sharedSecret); + uint8_t nonce[12]; + memset(nonce, 0, 12); + if (!i2p::crypto::AEADChaCha20Poly1305(plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, + true)) // encrypt + { + LogPrint(eLogWarning, "Tunnel: Plaintext AEAD encryption failed"); + return; + } + MixHash(encrypted, len + 16); // h = SHA256(h || ciphertext) + } - bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const - { - return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt - } + bool ECIESTunnelHopConfig::DecryptECIES(const uint8_t *key, const uint8_t *nonce, const uint8_t *encrypted, + size_t len, uint8_t *clearText) const { + return i2p::crypto::AEADChaCha20Poly1305(encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, + false); // decrypt + } - void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) - { - // generate keys - RAND_bytes (layerKey, 32); - RAND_bytes (ivKey, 32); - RAND_bytes (replyKey, 32); - RAND_bytes (replyIV, 16); - // fill clear text - uint8_t flag = 0; - if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; - if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; - memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET); - // encrypt - uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; - EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); - } + void LongECIESTunnelHopConfig::CreateBuildRequestRecord(uint8_t *records, uint32_t replyMsgID) { + // generate keys + RAND_bytes(layerKey, 32); + RAND_bytes(ivKey, 32); + RAND_bytes(replyKey, 32); + RAND_bytes(replyIV, 16); + // fill clear text + uint8_t flag = 0; + if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; + if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + htobe32buf(clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); + htobe32buf(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); + memcpy(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); + memcpy(clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); + memcpy(clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); + memcpy(clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); + memcpy(clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; + memset(clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility + htobe32buf(clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch()); + htobe32buf(clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes + htobe32buf(clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); + memset(clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, + ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET); + // encrypt + uint8_t *record = records + recordIndex * TUNNEL_BUILD_RECORD_SIZE; + EncryptECIES(clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, + record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); + memcpy(record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *) ident->GetIdentHash(), 16); + } - bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const - { - uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record)) - { - LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); - return false; - } - return true; - } + bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord(uint8_t *records) const { + uint8_t *record = records + recordIndex * TUNNEL_BUILD_RECORD_SIZE; + uint8_t nonce[12]; + memset(nonce, 0, 12); + if (!DecryptECIES(m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record)) { + LogPrint(eLogWarning, "Tunnel: Response AEAD decryption failed"); + return false; + } + return true; + } - void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) - { - // fill clear text - uint8_t flag = 0; - if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; - if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE ]; - htobe32buf (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - htobe32buf (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag; - memset (clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2); - clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES - htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); - htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes - htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET); - // encrypt - uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; - EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET); - // derive keys - i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK); - memcpy (replyKey, m_CK + 32, 32); - i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK); - memcpy (layerKey, m_CK + 32, 32); - if (isEndpoint) - { - i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK); - memcpy (ivKey, m_CK + 32, 32); - i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK - } - else - memcpy (ivKey, m_CK, 32); // last HKDF - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); - } + void ShortECIESTunnelHopConfig::CreateBuildRequestRecord(uint8_t *records, uint32_t replyMsgID) { + // fill clear text + uint8_t flag = 0; + if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; + if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + htobe32buf(clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); + htobe32buf(clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); + memcpy(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag; + memset(clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2); + clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES + htobe32buf(clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch()); + htobe32buf(clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes + htobe32buf(clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); + memset(clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, + SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET); + // encrypt + uint8_t *record = records + recordIndex * SHORT_TUNNEL_BUILD_RECORD_SIZE; + EncryptECIES(clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, + record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET); + // derive keys + i2p::crypto::HKDF(m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK); + memcpy(replyKey, m_CK + 32, 32); + i2p::crypto::HKDF(m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK); + memcpy(layerKey, m_CK + 32, 32); + if (isEndpoint) { + i2p::crypto::HKDF(m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK); + memcpy(ivKey, m_CK + 32, 32); + i2p::crypto::HKDF(m_CK, nullptr, 0, "RGarlicKeyAndTag", + m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK + } else + memcpy(ivKey, m_CK, 32); // last HKDF + memcpy(record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *) ident->GetIdentHash(), 16); + } - bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const - { - uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; - uint8_t nonce[12]; - memset (nonce, 0, 12); - nonce[4] = recordIndex; // nonce is record index - if (!DecryptECIES (replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record)) - { - LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); - return false; - } - return true; - } + bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord(uint8_t *records) const { + uint8_t *record = records + recordIndex * SHORT_TUNNEL_BUILD_RECORD_SIZE; + uint8_t nonce[12]; + memset(nonce, 0, 12); + nonce[4] = recordIndex; // nonce is record index + if (!DecryptECIES(replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record)) { + LogPrint(eLogWarning, "Tunnel: Response AEAD decryption failed"); + return false; + } + return true; + } - void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const - { - uint8_t * record = records + index*SHORT_TUNNEL_BUILD_RECORD_SIZE; - uint8_t nonce[12]; - memset (nonce, 0, 12); - nonce[4] = index; // nonce is index - i2p::crypto::ChaCha20 (record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record); - } + void ShortECIESTunnelHopConfig::DecryptRecord(uint8_t *records, int index) const { + uint8_t *record = records + index * SHORT_TUNNEL_BUILD_RECORD_SIZE; + uint8_t nonce[12]; + memset(nonce, 0, 12); + nonce[4] = index; // nonce is index + i2p::crypto::ChaCha20(record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record); + } - uint64_t ShortECIESTunnelHopConfig::GetGarlicKey (uint8_t * key) const - { - uint64_t tag; - memcpy (&tag, m_CK, 8); - memcpy (key, m_CK + 32, 32); - return tag; - } + uint64_t ShortECIESTunnelHopConfig::GetGarlicKey(uint8_t *key) const { + uint64_t tag; + memcpy(&tag, m_CK, 8); + memcpy(key, m_CK + 32, 32); + return tag; + } - void TunnelConfig::CreatePeers (const std::vector >& peers) - { - TunnelHopConfig * prev = nullptr; - for (const auto& it: peers) - { - TunnelHopConfig * hop = nullptr; - if (m_IsShort) - hop = new ShortECIESTunnelHopConfig (it); - else - { - if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - hop = new LongECIESTunnelHopConfig (it); - else - LogPrint (eLogError, "Tunnel: ElGamal router is not supported"); - } - if (hop) - { - if (prev) - prev->SetNext (hop); - else - m_FirstHop = hop; - prev = hop; - } - } - m_LastHop = prev; - } -} + void TunnelConfig::CreatePeers(const std::vector > &peers) { + TunnelHopConfig *prev = nullptr; + for (const auto &it: peers) { + TunnelHopConfig *hop = nullptr; + if (m_IsShort) + hop = new ShortECIESTunnelHopConfig(it); + else { + if (it->GetCryptoKeyType() == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + hop = new LongECIESTunnelHopConfig(it); + else + LogPrint(eLogError, "Tunnel: ElGamal router is not supported"); + } + if (hop) { + if (prev) + prev->SetNext(hop); + else + m_FirstHop = hop; + prev = hop; + } + } + m_LastHop = prev; + } + } } \ No newline at end of file diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 9dcf2c02..b37805d8 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -14,210 +14,210 @@ #include "RouterContext.h" #include "Crypto.h" -namespace i2p -{ -namespace tunnel -{ - struct TunnelHopConfig - { - std::shared_ptr ident; - i2p::data::IdentHash nextIdent; - uint32_t tunnelID, nextTunnelID; - uint8_t layerKey[32]; - uint8_t ivKey[32]; - uint8_t replyKey[32]; - uint8_t replyIV[16]; - bool isGateway, isEndpoint; +namespace i2p { + namespace tunnel { + struct TunnelHopConfig { + std::shared_ptr ident; + i2p::data::IdentHash nextIdent; + uint32_t tunnelID, nextTunnelID; + uint8_t layerKey[32]; + uint8_t ivKey[32]; + uint8_t replyKey[32]; + uint8_t replyIV[16]; + bool isGateway, isEndpoint; - TunnelHopConfig * next, * prev; - int recordIndex; // record # in tunnel build message + TunnelHopConfig *next, *prev; + int recordIndex; // record # in tunnel build message - TunnelHopConfig (std::shared_ptr r); - virtual ~TunnelHopConfig () {}; + TunnelHopConfig(std::shared_ptr r); - void SetNextIdent (const i2p::data::IdentHash& ident); - void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent); - void SetNext (TunnelHopConfig * n); - void SetPrev (TunnelHopConfig * p); + virtual ~TunnelHopConfig() {}; - virtual uint8_t GetRetCode (const uint8_t * records) const = 0; - virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0; - virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0; - virtual void DecryptRecord (uint8_t * records, int index) const; // AES - virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag - }; + void SetNextIdent(const i2p::data::IdentHash &ident); - struct ECIESTunnelHopConfig: public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState - { - ECIESTunnelHopConfig (std::shared_ptr r): - TunnelHopConfig (r) {}; - void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted); - bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const; - }; + void SetReplyHop(uint32_t replyTunnelID, const i2p::data::IdentHash &replyIdent); - struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig - { - LongECIESTunnelHopConfig (std::shared_ptr r): - ECIESTunnelHopConfig (r) {}; - uint8_t GetRetCode (const uint8_t * records) const override - { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; - bool DecryptBuildResponseRecord (uint8_t * records) const override; - }; + void SetNext(TunnelHopConfig *n); - struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig - { - ShortECIESTunnelHopConfig (std::shared_ptr r): - ECIESTunnelHopConfig (r) {}; - uint8_t GetRetCode (const uint8_t * records) const override - { return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; - bool DecryptBuildResponseRecord (uint8_t * records) const override; - void DecryptRecord (uint8_t * records, int index) const override; // Chacha20 - uint64_t GetGarlicKey (uint8_t * key) const override; - }; + void SetPrev(TunnelHopConfig *p); - class TunnelConfig - { - public: + virtual uint8_t GetRetCode(const uint8_t *records) const = 0; - TunnelConfig (const std::vector >& peers, - bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // inbound - m_IsShort (isShort), m_FarEndTransports (farEndTransports) - { - CreatePeers (peers); - m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); - } + virtual void CreateBuildRequestRecord(uint8_t *records, uint32_t replyMsgID) = 0; - TunnelConfig (const std::vector >& peers, - uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, - i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // outbound - m_IsShort (isShort), m_FarEndTransports (farEndTransports) - { - CreatePeers (peers); - m_FirstHop->isGateway = false; - m_LastHop->SetReplyHop (replyTunnelID, replyIdent); - } + virtual bool DecryptBuildResponseRecord(uint8_t *records) const = 0; - virtual ~TunnelConfig () - { - TunnelHopConfig * hop = m_FirstHop; + virtual void DecryptRecord(uint8_t *records, int index) const; // AES + virtual uint64_t GetGarlicKey(uint8_t *key) const { return 0; }; // return tag + }; - while (hop) - { - auto tmp = hop; - hop = hop->next; - delete tmp; - } - } + struct ECIESTunnelHopConfig : public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState { + ECIESTunnelHopConfig(std::shared_ptr r) : + TunnelHopConfig(r) {}; - bool IsShort () const { return m_IsShort; } + void EncryptECIES(const uint8_t *clearText, size_t len, uint8_t *encrypted); - i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const - { - return m_FarEndTransports; - } + bool DecryptECIES(const uint8_t *key, const uint8_t *nonce, const uint8_t *encrypted, size_t len, + uint8_t *clearText) const; + }; - TunnelHopConfig * GetFirstHop () const - { - return m_FirstHop; - } + struct LongECIESTunnelHopConfig : public ECIESTunnelHopConfig { + LongECIESTunnelHopConfig(std::shared_ptr r) : + ECIESTunnelHopConfig(r) {}; - TunnelHopConfig * GetLastHop () const - { - return m_LastHop; - } + uint8_t GetRetCode(const uint8_t *records) const override { return (records + recordIndex * + TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; + }; - int GetNumHops () const - { - int num = 0; - TunnelHopConfig * hop = m_FirstHop; - while (hop) - { - num++; - hop = hop->next; - } - return num; - } + void CreateBuildRequestRecord(uint8_t *records, uint32_t replyMsgID) override; - bool IsEmpty () const - { - return !m_FirstHop; - } + bool DecryptBuildResponseRecord(uint8_t *records) const override; + }; - virtual bool IsInbound () const { return m_FirstHop->isGateway; } + struct ShortECIESTunnelHopConfig : public ECIESTunnelHopConfig { + ShortECIESTunnelHopConfig(std::shared_ptr r) : + ECIESTunnelHopConfig(r) {}; - virtual uint32_t GetTunnelID () const - { - if (!m_FirstHop) return 0; - return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID; - } + uint8_t GetRetCode(const uint8_t *records) const override { return (records + recordIndex * + SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; + }; - virtual uint32_t GetNextTunnelID () const - { - if (!m_FirstHop) return 0; - return m_FirstHop->tunnelID; - } + void CreateBuildRequestRecord(uint8_t *records, uint32_t replyMsgID) override; - virtual const i2p::data::IdentHash& GetNextIdentHash () const - { - return m_FirstHop->ident->GetIdentHash (); - } + bool DecryptBuildResponseRecord(uint8_t *records) const override; - virtual const i2p::data::IdentHash& GetLastIdentHash () const - { - return m_LastHop->ident->GetIdentHash (); - } + void DecryptRecord(uint8_t *records, int index) const override; // Chacha20 + uint64_t GetGarlicKey(uint8_t *key) const override; + }; - std::vector > GetPeers () const - { - std::vector > peers; - TunnelHopConfig * hop = m_FirstHop; - while (hop) - { - peers.push_back (hop->ident); - hop = hop->next; - } - return peers; - } + class TunnelConfig { + public: - protected: + TunnelConfig(const std::vector > &peers, + bool isShort, + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports) + : // inbound + m_IsShort(isShort), m_FarEndTransports(farEndTransports) { + CreatePeers(peers); + m_LastHop->SetNextIdent(i2p::context.GetIdentHash()); + } - // this constructor can't be called from outside - TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false), - m_FarEndTransports (i2p::data::RouterInfo::eAllTransports) - { - } + TunnelConfig(const std::vector > &peers, + uint32_t replyTunnelID, const i2p::data::IdentHash &replyIdent, bool isShort, + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports) + : // outbound + m_IsShort(isShort), m_FarEndTransports(farEndTransports) { + CreatePeers(peers); + m_FirstHop->isGateway = false; + m_LastHop->SetReplyHop(replyTunnelID, replyIdent); + } - private: + virtual ~TunnelConfig() { + TunnelHopConfig *hop = m_FirstHop; - void CreatePeers (const std::vector >& peers); + while (hop) { + auto tmp = hop; + hop = hop->next; + delete tmp; + } + } - private: + bool IsShort() const { return m_IsShort; } - TunnelHopConfig * m_FirstHop, * m_LastHop; - bool m_IsShort; - i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; - }; + i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports() const { + return m_FarEndTransports; + } - class ZeroHopsTunnelConfig: public TunnelConfig - { - public: + TunnelHopConfig *GetFirstHop() const { + return m_FirstHop; + } - ZeroHopsTunnelConfig () { RAND_bytes ((uint8_t *)&m_TunnelID, 4);}; + TunnelHopConfig *GetLastHop() const { + return m_LastHop; + } - bool IsInbound () const { return true; }; // TODO: - uint32_t GetTunnelID () const { return m_TunnelID; }; - uint32_t GetNextTunnelID () const { return m_TunnelID; }; - const i2p::data::IdentHash& GetNextIdentHash () const { return i2p::context.GetIdentHash (); }; - const i2p::data::IdentHash& GetLastIdentHash () const { return i2p::context.GetIdentHash (); }; + int GetNumHops() const { + int num = 0; + TunnelHopConfig *hop = m_FirstHop; + while (hop) { + num++; + hop = hop->next; + } + return num; + } + + bool IsEmpty() const { + return !m_FirstHop; + } + + virtual bool IsInbound() const { return m_FirstHop->isGateway; } + + virtual uint32_t GetTunnelID() const { + if (!m_FirstHop) return 0; + return IsInbound() ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID; + } + + virtual uint32_t GetNextTunnelID() const { + if (!m_FirstHop) return 0; + return m_FirstHop->tunnelID; + } + + virtual const i2p::data::IdentHash &GetNextIdentHash() const { + return m_FirstHop->ident->GetIdentHash(); + } + + virtual const i2p::data::IdentHash &GetLastIdentHash() const { + return m_LastHop->ident->GetIdentHash(); + } + + std::vector > GetPeers() const { + std::vector > peers; + TunnelHopConfig *hop = m_FirstHop; + while (hop) { + peers.push_back(hop->ident); + hop = hop->next; + } + return peers; + } + + protected: + + // this constructor can't be called from outside + TunnelConfig() : m_FirstHop(nullptr), m_LastHop(nullptr), m_IsShort(false), + m_FarEndTransports(i2p::data::RouterInfo::eAllTransports) { + } + + private: + + void CreatePeers(const std::vector > &peers); + + private: + + TunnelHopConfig *m_FirstHop, *m_LastHop; + bool m_IsShort; + i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; + }; + + class ZeroHopsTunnelConfig : public TunnelConfig { + public: + + ZeroHopsTunnelConfig() { RAND_bytes((uint8_t * ) & m_TunnelID, 4); }; + + bool IsInbound() const { return true; }; // TODO: + uint32_t GetTunnelID() const { return m_TunnelID; }; + + uint32_t GetNextTunnelID() const { return m_TunnelID; }; + + const i2p::data::IdentHash &GetNextIdentHash() const { return i2p::context.GetIdentHash(); }; + + const i2p::data::IdentHash &GetLastIdentHash() const { return i2p::context.GetIdentHash(); }; - private: + private: - uint32_t m_TunnelID; - }; -} + uint32_t m_TunnelID; + }; + } } #endif diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index 9c294374..3edaa4de 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -17,358 +17,314 @@ #include "Timestamp.h" #include "TunnelEndpoint.h" -namespace i2p -{ -namespace tunnel -{ - TunnelEndpoint::~TunnelEndpoint () - { - } +namespace i2p { + namespace tunnel { + TunnelEndpoint::~TunnelEndpoint() { + } - void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr msg) - { - m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; + void TunnelEndpoint::HandleDecryptedTunnelDataMsg(std::shared_ptr msg) { + m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; - uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16 - uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // without 4-byte checksum - if (zero) - { - uint8_t * fragment = zero + 1; - // verify checksum - memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end - uint8_t hash[32]; - SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv - if (memcmp (hash, decrypted, 4)) - { - LogPrint (eLogError, "TunnelMessage: Checksum verification failed"); - return; - } - // process fragments - while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - uint8_t flag = fragment[0]; - fragment++; + uint8_t *decrypted = msg->GetPayload() + 20; // 4 + 16 + uint8_t *zero = (uint8_t *) memchr(decrypted + 4, 0, + TUNNEL_DATA_ENCRYPTED_SIZE - 4); // without 4-byte checksum + if (zero) { + uint8_t *fragment = zero + 1; + // verify checksum + memcpy(msg->GetPayload() + TUNNEL_DATA_MSG_SIZE, msg->GetPayload() + 4, 16); // copy iv to the end + uint8_t hash[32]; + SHA256(fragment, TUNNEL_DATA_MSG_SIZE - (fragment - msg->GetPayload()) + 16, hash); // payload + iv + if (memcmp(hash, decrypted, 4)) { + LogPrint(eLogError, "TunnelMessage: Checksum verification failed"); + return; + } + // process fragments + while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) { + uint8_t flag = fragment[0]; + fragment++; - bool isFollowOnFragment = flag & 0x80, isLastFragment = true; - uint32_t msgID = 0; - int fragmentNum = 0; - if (!isFollowOnFragment) - { - // first fragment - if (m_CurrentMsgID) - AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete + bool isFollowOnFragment = flag & 0x80, isLastFragment = true; + uint32_t msgID = 0; + int fragmentNum = 0; + if (!isFollowOnFragment) { + // first fragment + if (m_CurrentMsgID) + AddIncompleteCurrentMessage(); // we have got a new message while previous is not complete - m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); - switch (m_CurrentMessage.deliveryType) - { - case eDeliveryTypeLocal: // 0 - break; - case eDeliveryTypeTunnel: // 1 - m_CurrentMessage.tunnelID = bufbe32toh (fragment); - fragment += 4; // tunnelID - m_CurrentMessage.hash = i2p::data::IdentHash (fragment); - fragment += 32; // hash - break; - case eDeliveryTypeRouter: // 2 - m_CurrentMessage.hash = i2p::data::IdentHash (fragment); - fragment += 32; // to hash - break; - default: ; - } + m_CurrentMessage.deliveryType = (TunnelDeliveryType) ((flag >> 5) & 0x03); + switch (m_CurrentMessage.deliveryType) { + case eDeliveryTypeLocal: // 0 + break; + case eDeliveryTypeTunnel: // 1 + m_CurrentMessage.tunnelID = bufbe32toh(fragment); + fragment += 4; // tunnelID + m_CurrentMessage.hash = i2p::data::IdentHash(fragment); + fragment += 32; // hash + break; + case eDeliveryTypeRouter: // 2 + m_CurrentMessage.hash = i2p::data::IdentHash(fragment); + fragment += 32; // to hash + break; + default:; + } - bool isFragmented = flag & 0x08; - if (isFragmented) - { - // Message ID - msgID = bufbe32toh (fragment); - fragment += 4; - m_CurrentMsgID = msgID; - isLastFragment = false; - } - } - else - { - // follow on - msgID = bufbe32toh (fragment); // MessageID - fragment += 4; - fragmentNum = (flag >> 1) & 0x3F; // 6 bits - isLastFragment = flag & 0x01; - } + bool isFragmented = flag & 0x08; + if (isFragmented) { + // Message ID + msgID = bufbe32toh(fragment); + fragment += 4; + m_CurrentMsgID = msgID; + isLastFragment = false; + } + } else { + // follow on + msgID = bufbe32toh(fragment); // MessageID + fragment += 4; + fragmentNum = (flag >> 1) & 0x3F; // 6 bits + isLastFragment = flag & 0x01; + } - uint16_t size = bufbe16toh (fragment); - fragment += 2; + uint16_t size = bufbe16toh(fragment); + fragment += 2; - // handle fragment - if (isFollowOnFragment) - { - // existing message - if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum) - HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous - else - { - HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } - else - { - // new message - msg->offset = fragment - msg->buf; - msg->len = msg->offset + size; - // check message size - if (msg->len > msg->maxLen) - { - LogPrint (eLogError, "TunnelMessage: Fragment is too long ", (int)size); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - return; - } - // create new or assign I2NP message - if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - // this is not last message. we have to copy it - m_CurrentMessage.data = NewI2NPTunnelMessage (true); - *(m_CurrentMessage.data) = *msg; - } - else - m_CurrentMessage.data = msg; + // handle fragment + if (isFollowOnFragment) { + // existing message + if (m_CurrentMsgID && m_CurrentMsgID == msgID && + m_CurrentMessage.nextFragmentNum == fragmentNum) + HandleCurrenMessageFollowOnFragment(fragment, size, isLastFragment); // previous + else { + HandleFollowOnFragment(msgID, isLastFragment, fragmentNum, fragment, size); // another + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } + } else { + // new message + msg->offset = fragment - msg->buf; + msg->len = msg->offset + size; + // check message size + if (msg->len > msg->maxLen) { + LogPrint(eLogError, "TunnelMessage: Fragment is too long ", (int) size); + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + return; + } + // create new or assign I2NP message + if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) { + // this is not last message. we have to copy it + m_CurrentMessage.data = NewI2NPTunnelMessage(true); + *(m_CurrentMessage.data) = *msg; + } else + m_CurrentMessage.data = msg; - if (isLastFragment) - { - // single message - HandleNextMessage (m_CurrentMessage); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - else if (msgID) - { - // first fragment of a new message - m_CurrentMessage.nextFragmentNum = 1; - m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); - HandleOutOfSequenceFragments (msgID, m_CurrentMessage); - } - else - { - LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented"); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } + if (isLastFragment) { + // single message + HandleNextMessage(m_CurrentMessage); + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } else if (msgID) { + // first fragment of a new message + m_CurrentMessage.nextFragmentNum = 1; + m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch(); + HandleOutOfSequenceFragments(msgID, m_CurrentMessage); + } else { + LogPrint(eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented"); + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } + } - fragment += size; - } - } - else - LogPrint (eLogError, "TunnelMessage: Zero not found"); - } + fragment += size; + } + } else + LogPrint(eLogError, "TunnelMessage: Zero not found"); + } - void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, - uint8_t fragmentNum, const uint8_t * fragment, size_t size) - { - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end()) - { - auto& msg = it->second; - if (fragmentNum == msg.nextFragmentNum) - { - if (ConcatFollowOnFragment (msg, fragment, size)) - { - if (isLastFragment) - { - // message complete - HandleNextMessage (msg); - m_IncompleteMessages.erase (it); - } - else - { - msg.nextFragmentNum++; - HandleOutOfSequenceFragments (msgID, msg); - } - } - else - { - LogPrint (eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped"); - m_IncompleteMessages.erase (it); - } - } - else - { - LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved"); - AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size); - } - } - else - { - LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved"); - AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size); - } - } + void TunnelEndpoint::HandleFollowOnFragment(uint32_t msgID, bool isLastFragment, + uint8_t fragmentNum, const uint8_t *fragment, size_t size) { + auto it = m_IncompleteMessages.find(msgID); + if (it != m_IncompleteMessages.end()) { + auto &msg = it->second; + if (fragmentNum == msg.nextFragmentNum) { + if (ConcatFollowOnFragment(msg, fragment, size)) { + if (isLastFragment) { + // message complete + HandleNextMessage(msg); + m_IncompleteMessages.erase(it); + } else { + msg.nextFragmentNum++; + HandleOutOfSequenceFragments(msgID, msg); + } + } else { + LogPrint(eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, + "exceeds max I2NP message size, message dropped"); + m_IncompleteMessages.erase(it); + } + } else { + LogPrint(eLogWarning, "TunnelMessage: Unexpected fragment ", (int) fragmentNum, " instead ", + (int) msg.nextFragmentNum, " of message ", msgID, ", saved"); + AddOutOfSequenceFragment(msgID, fragmentNum, isLastFragment, fragment, size); + } + } else { + LogPrint(eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved"); + AddOutOfSequenceFragment(msgID, fragmentNum, isLastFragment, fragment, size); + } + } - bool TunnelEndpoint::ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const - { - if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long - { - if (msg.data->len + size > msg.data->maxLen) - { - // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (); - *newMsg = *(msg.data); - msg.data = newMsg; - } - if (msg.data->Concat (fragment, size) < size) // concatenate fragment - { - LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen); - return false; - } - } - else - return false; - return true; - } + bool + TunnelEndpoint::ConcatFollowOnFragment(TunnelMessageBlockEx &msg, const uint8_t *fragment, size_t size) const { + if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long + { + if (msg.data->len + size > msg.data->maxLen) { + // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough"); + auto newMsg = NewI2NPMessage(); + *newMsg = *(msg.data); + msg.data = newMsg; + } + if (msg.data->Concat(fragment, size) < size) // concatenate fragment + { + LogPrint(eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen); + return false; + } + } else + return false; + return true; + } - void TunnelEndpoint::HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment) - { - if (ConcatFollowOnFragment (m_CurrentMessage, fragment, size)) - { - if (isLastFragment) - { - // message complete - HandleNextMessage (m_CurrentMessage); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - else - { - m_CurrentMessage.nextFragmentNum++; - HandleOutOfSequenceFragments (m_CurrentMsgID, m_CurrentMessage); - } - } - else - { - LogPrint (eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", m_CurrentMsgID, " exceeds max I2NP message size, message dropped"); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } + void + TunnelEndpoint::HandleCurrenMessageFollowOnFragment(const uint8_t *fragment, size_t size, bool isLastFragment) { + if (ConcatFollowOnFragment(m_CurrentMessage, fragment, size)) { + if (isLastFragment) { + // message complete + HandleNextMessage(m_CurrentMessage); + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } else { + m_CurrentMessage.nextFragmentNum++; + HandleOutOfSequenceFragments(m_CurrentMsgID, m_CurrentMessage); + } + } else { + LogPrint(eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", + m_CurrentMsgID, " exceeds max I2NP message size, message dropped"); + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } + } - void TunnelEndpoint::AddIncompleteCurrentMessage () - { - if (m_CurrentMsgID) - { - auto ret = m_IncompleteMessages.emplace (m_CurrentMsgID, m_CurrentMessage); - if (!ret.second) - LogPrint (eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists"); - m_CurrentMessage.data = nullptr; - m_CurrentMsgID = 0; - } - } + void TunnelEndpoint::AddIncompleteCurrentMessage() { + if (m_CurrentMsgID) { + auto ret = m_IncompleteMessages.emplace(m_CurrentMsgID, m_CurrentMessage); + if (!ret.second) + LogPrint(eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists"); + m_CurrentMessage.data = nullptr; + m_CurrentMsgID = 0; + } + } - void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, - bool isLastFragment, const uint8_t * fragment, size_t size) - { - std::unique_ptr f(new Fragment (isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), size)); - memcpy (f->data.data (), fragment, size); - if (!m_OutOfSequenceFragments.emplace ((uint64_t)msgID << 32 | fragmentNum, std::move (f)).second) - LogPrint (eLogInfo, "TunnelMessage: Duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); - } + void TunnelEndpoint::AddOutOfSequenceFragment(uint32_t msgID, uint8_t fragmentNum, + bool isLastFragment, const uint8_t *fragment, size_t size) { + std::unique_ptr f(new Fragment(isLastFragment, i2p::util::GetMillisecondsSinceEpoch(), size)); + memcpy(f->data.data(), fragment, size); + if (!m_OutOfSequenceFragments.emplace((uint64_t) msgID << 32 | fragmentNum, std::move(f)).second) + LogPrint(eLogInfo, "TunnelMessage: Duplicate out-of-sequence fragment ", fragmentNum, " of message ", + msgID); + } - void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg) - { - while (ConcatNextOutOfSequenceFragment (msgID, msg)) - { - if (!msg.nextFragmentNum) // message complete - { - HandleNextMessage (msg); - if (&msg == &m_CurrentMessage) - { - m_CurrentMsgID = 0; - m_CurrentMessage.data = nullptr; - } - else - m_IncompleteMessages.erase (msgID); - LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found"); - break; - } - } - } + void TunnelEndpoint::HandleOutOfSequenceFragments(uint32_t msgID, TunnelMessageBlockEx &msg) { + while (ConcatNextOutOfSequenceFragment(msgID, msg)) { + if (!msg.nextFragmentNum) // message complete + { + HandleNextMessage(msg); + if (&msg == &m_CurrentMessage) { + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } else + m_IncompleteMessages.erase(msgID); + LogPrint(eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found"); + break; + } + } + } - bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) - { - auto it = m_OutOfSequenceFragments.find ((uint64_t)msgID << 32 | msg.nextFragmentNum); - if (it != m_OutOfSequenceFragments.end ()) - { - LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found"); - size_t size = it->second->data.size (); - if (msg.data->len + size > msg.data->maxLen) - { - LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (); - *newMsg = *(msg.data); - msg.data = newMsg; - } - if (msg.data->Concat (it->second->data.data (), size) < size) // concatenate out-of-sync fragment - LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen); - if (it->second->isLastFragment) - // message complete - msg.nextFragmentNum = 0; - else - msg.nextFragmentNum++; - m_OutOfSequenceFragments.erase (it); - return true; - } - return false; - } + bool TunnelEndpoint::ConcatNextOutOfSequenceFragment(uint32_t msgID, TunnelMessageBlockEx &msg) { + auto it = m_OutOfSequenceFragments.find((uint64_t) msgID << 32 | msg.nextFragmentNum); + if (it != m_OutOfSequenceFragments.end()) { + LogPrint(eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int) msg.nextFragmentNum, + " of message ", msgID, " found"); + size_t size = it->second->data.size(); + if (msg.data->len + size > msg.data->maxLen) { + LogPrint(eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, + " is not enough"); + auto newMsg = NewI2NPMessage(); + *newMsg = *(msg.data); + msg.data = newMsg; + } + if (msg.data->Concat(it->second->data.data(), size) < size) // concatenate out-of-sync fragment + LogPrint(eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen); + if (it->second->isLastFragment) + // message complete + msg.nextFragmentNum = 0; + else + msg.nextFragmentNum++; + m_OutOfSequenceFragments.erase(it); + return true; + } + return false; + } - void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg) - { - if (!m_IsInbound && msg.data->IsExpired ()) - { - LogPrint (eLogInfo, "TunnelMessage: Message expired"); - return; - } - uint8_t typeID = msg.data->GetTypeID (); - LogPrint (eLogDebug, "TunnelMessage: Handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID); - // catch RI or reply with new list of routers - if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) && - !m_IsInbound && msg.deliveryType != eDeliveryTypeLocal) - i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg.data)); + void TunnelEndpoint::HandleNextMessage(const TunnelMessageBlock &msg) { + if (!m_IsInbound && msg.data->IsExpired()) { + LogPrint(eLogInfo, "TunnelMessage: Message expired"); + return; + } + uint8_t typeID = msg.data->GetTypeID(); + LogPrint(eLogDebug, "TunnelMessage: Handle fragment of ", msg.data->GetLength(), " bytes, msg type ", + (int) typeID); + // catch RI or reply with new list of routers + if ((IsRouterInfoMsg(msg.data) || typeID == eI2NPDatabaseSearchReply) && + !m_IsInbound && msg.deliveryType != eDeliveryTypeLocal) + i2p::data::netdb.PostI2NPMsg(CopyI2NPMessage(msg.data)); - switch (msg.deliveryType) - { - case eDeliveryTypeLocal: - i2p::HandleI2NPMessage (msg.data); - break; - case eDeliveryTypeTunnel: - if (!m_IsInbound) // outbound transit tunnel - i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); - else - LogPrint (eLogError, "TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped"); - break; - case eDeliveryTypeRouter: - if (!m_IsInbound) // outbound transit tunnel - i2p::transport::transports.SendMessage (msg.hash, msg.data); - else // we shouldn't send this message. possible leakage - LogPrint (eLogError, "TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped"); - break; - default: - LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); - }; - } + switch (msg.deliveryType) { + case eDeliveryTypeLocal: + i2p::HandleI2NPMessage(msg.data); + break; + case eDeliveryTypeTunnel: + if (!m_IsInbound) // outbound transit tunnel + i2p::transport::transports.SendMessage(msg.hash, + i2p::CreateTunnelGatewayMsg(msg.tunnelID, msg.data)); + else + LogPrint(eLogError, + "TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped"); + break; + case eDeliveryTypeRouter: + if (!m_IsInbound) // outbound transit tunnel + i2p::transport::transports.SendMessage(msg.hash, msg.data); + else // we shouldn't send this message. possible leakage + LogPrint(eLogError, + "TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped"); + break; + default: + LogPrint(eLogError, "TunnelMessage: Unknown delivery type ", (int) msg.deliveryType); + }; + } - void TunnelEndpoint::Cleanup () - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - // out-of-sequence fragments - for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();) - { - if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) - it = m_OutOfSequenceFragments.erase (it); - else - ++it; - } - // incomplete messages - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) - { - if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) - it = m_IncompleteMessages.erase (it); - else - ++it; - } - } -} + void TunnelEndpoint::Cleanup() { + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + // out-of-sequence fragments + for (auto it = m_OutOfSequenceFragments.begin(); it != m_OutOfSequenceFragments.end();) { + if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) + it = m_OutOfSequenceFragments.erase(it); + else + ++it; + } + // incomplete messages + for (auto it = m_IncompleteMessages.begin(); it != m_IncompleteMessages.end();) { + if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) + it = m_IncompleteMessages.erase(it); + else + ++it; + } + } + } } diff --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h index 17590a5f..d342c2b6 100644 --- a/libi2pd/TunnelEndpoint.h +++ b/libi2pd/TunnelEndpoint.h @@ -16,57 +16,64 @@ #include "I2NPProtocol.h" #include "TunnelBase.h" -namespace i2p -{ -namespace tunnel -{ - class TunnelEndpoint - { - struct TunnelMessageBlockEx: public TunnelMessageBlock - { - uint64_t receiveTime; // milliseconds since epoch - uint8_t nextFragmentNum; - }; +namespace i2p { + namespace tunnel { + class TunnelEndpoint { + struct TunnelMessageBlockEx : public TunnelMessageBlock { + uint64_t receiveTime; // milliseconds since epoch + uint8_t nextFragmentNum; + }; - struct Fragment - { - Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {}; - bool isLastFragment; - uint64_t receiveTime; // milliseconds since epoch - std::vector data; - }; + struct Fragment { + Fragment(bool last, uint64_t t, size_t size) : isLastFragment(last), receiveTime(t), data(size) {}; + bool isLastFragment; + uint64_t receiveTime; // milliseconds since epoch + std::vector data; + }; - public: + public: - TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {}; - ~TunnelEndpoint (); - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - void Cleanup (); + TunnelEndpoint(bool isInbound) : m_IsInbound(isInbound), m_NumReceivedBytes(0), m_CurrentMsgID(0) {}; - void HandleDecryptedTunnelDataMsg (std::shared_ptr msg); + ~TunnelEndpoint(); - private: + size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; }; - void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size); - bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success - void HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment); - void HandleNextMessage (const TunnelMessageBlock& msg); + void Cleanup(); - void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size); - bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added - void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg); - void AddIncompleteCurrentMessage (); + void HandleDecryptedTunnelDataMsg(std::shared_ptr msg); - private: + private: - std::unordered_map m_IncompleteMessages; - std::unordered_map > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment - bool m_IsInbound; - size_t m_NumReceivedBytes; - TunnelMessageBlockEx m_CurrentMessage; - uint32_t m_CurrentMsgID; - }; -} + void + HandleFollowOnFragment(uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t *fragment, + size_t size); + + bool ConcatFollowOnFragment(TunnelMessageBlockEx &msg, const uint8_t *fragment, + size_t size) const; // true if success + void HandleCurrenMessageFollowOnFragment(const uint8_t *fragment, size_t size, bool isLastFragment); + + void HandleNextMessage(const TunnelMessageBlock &msg); + + void + AddOutOfSequenceFragment(uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t *fragment, + size_t size); + + bool ConcatNextOutOfSequenceFragment(uint32_t msgID, TunnelMessageBlockEx &msg); // true if something added + void HandleOutOfSequenceFragments(uint32_t msgID, TunnelMessageBlockEx &msg); + + void AddIncompleteCurrentMessage(); + + private: + + std::unordered_map m_IncompleteMessages; + std::unordered_map > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment + bool m_IsInbound; + size_t m_NumReceivedBytes; + TunnelMessageBlockEx m_CurrentMessage; + uint32_t m_CurrentMsgID; + }; + } } #endif diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 12e7652f..5885c280 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -14,220 +14,197 @@ #include "Transports.h" #include "TunnelGateway.h" -namespace i2p -{ -namespace tunnel -{ - TunnelGatewayBuffer::TunnelGatewayBuffer (): - m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0), m_NonZeroRandomBuffer (nullptr) - { - } +namespace i2p { + namespace tunnel { + TunnelGatewayBuffer::TunnelGatewayBuffer() : + m_CurrentTunnelDataMsg(nullptr), m_RemainingSize(0), m_NonZeroRandomBuffer(nullptr) { + } - TunnelGatewayBuffer::~TunnelGatewayBuffer () - { - ClearTunnelDataMsgs (); - if (m_NonZeroRandomBuffer) delete[] m_NonZeroRandomBuffer; - } + TunnelGatewayBuffer::~TunnelGatewayBuffer() { + ClearTunnelDataMsgs(); + if (m_NonZeroRandomBuffer) delete[] m_NonZeroRandomBuffer; + } - void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) - { - bool messageCreated = false; - if (!m_CurrentTunnelDataMsg) - { - CreateCurrentTunnelDataMessage (); - messageCreated = true; - } + void TunnelGatewayBuffer::PutI2NPMsg(const TunnelMessageBlock &block) { + bool messageCreated = false; + if (!m_CurrentTunnelDataMsg) { + CreateCurrentTunnelDataMessage(); + messageCreated = true; + } - // create delivery instructions - uint8_t di[43]; // max delivery instruction length is 43 for tunnel - size_t diLen = 1;// flag - if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router - { - if (block.deliveryType == eDeliveryTypeTunnel) - { - htobe32buf (di + diLen, block.tunnelID); - diLen += 4; // tunnelID - } + // create delivery instructions + uint8_t di[43]; // max delivery instruction length is 43 for tunnel + size_t diLen = 1;// flag + if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router + { + if (block.deliveryType == eDeliveryTypeTunnel) { + htobe32buf(di + diLen, block.tunnelID); + diLen += 4; // tunnelID + } - memcpy (di + diLen, block.hash, 32); - diLen += 32; //len - } - di[0] = block.deliveryType << 5; // set delivery type + memcpy(di + diLen, block.hash, 32); + diLen += 32; //len + } + di[0] = block.deliveryType << 5; // set delivery type - // create fragments - const std::shared_ptr & msg = block.data; - size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length + // create fragments + const std::shared_ptr &msg = block.data; + size_t fullMsgLen = diLen + msg->GetLength() + 2; // delivery instructions + payload + 2 bytes length - if (!messageCreated && fullMsgLen > m_RemainingSize) // check if we should complete previous message - { - size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; - // length of bytes doesn't fit full tunnel message - // every follow-on fragment adds 7 bytes - size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; - if (!nonFit || nonFit > m_RemainingSize || m_RemainingSize < fullMsgLen/5) - { - CompleteCurrentTunnelDataMessage (); - CreateCurrentTunnelDataMessage (); - } - } - if (fullMsgLen <= m_RemainingSize) - { - // message fits. First and last fragment - htobe16buf (di + diLen, msg->GetLength ()); - diLen += 2; // size - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ()); - m_CurrentTunnelDataMsg->len += diLen + msg->GetLength (); - m_RemainingSize -= diLen + msg->GetLength (); - if (!m_RemainingSize) - CompleteCurrentTunnelDataMessage (); - } - else - { - if (diLen + 6 <= m_RemainingSize) - { - // delivery instructions fit - uint32_t msgID; - memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order - size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) + if (!messageCreated && fullMsgLen > m_RemainingSize) // check if we should complete previous message + { + size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; + // length of bytes doesn't fit full tunnel message + // every follow-on fragment adds 7 bytes + size_t nonFit = (fullMsgLen + numFollowOnFragments * 7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; + if (!nonFit || nonFit > m_RemainingSize || m_RemainingSize < fullMsgLen / 5) { + CompleteCurrentTunnelDataMessage(); + CreateCurrentTunnelDataMessage(); + } + } + if (fullMsgLen <= m_RemainingSize) { + // message fits. First and last fragment + htobe16buf(di + diLen, msg->GetLength()); + diLen += 2; // size + memcpy(m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); + memcpy(m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer(), + msg->GetLength()); + m_CurrentTunnelDataMsg->len += diLen + msg->GetLength(); + m_RemainingSize -= diLen + msg->GetLength(); + if (!m_RemainingSize) + CompleteCurrentTunnelDataMessage(); + } else { + if (diLen + 6 <= m_RemainingSize) { + // delivery instructions fit + uint32_t msgID; + memcpy(&msgID, msg->GetHeader() + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order + size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) - // first fragment - di[0] |= 0x08; // fragmented - htobuf32 (di + diLen, msgID); - diLen += 4; // Message ID - htobe16buf (di + diLen, size); - diLen += 2; // size - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size); - m_CurrentTunnelDataMsg->len += diLen + size; - CompleteCurrentTunnelDataMessage (); - // follow on fragments - int fragmentNumber = 1; - while (size < msg->GetLength ()) - { - CreateCurrentTunnelDataMessage (); - uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); - buf[0] = 0x80 | (fragmentNumber << 1); // frag - bool isLastFragment = false; - size_t s = msg->GetLength () - size; - if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions - s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; - else // last fragment - { - buf[0] |= 0x01; - isLastFragment = true; - } - htobuf32 (buf + 1, msgID); //Message ID - htobe16buf (buf + 5, s); // size - memcpy (buf + 7, msg->GetBuffer () + size, s); - m_CurrentTunnelDataMsg->len += s+7; - if (isLastFragment) - { - if(m_RemainingSize < (s+7)) { - LogPrint (eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", s+7); - } else { - m_RemainingSize -= s+7; - if (m_RemainingSize == 0) - CompleteCurrentTunnelDataMessage (); - } - } - else - CompleteCurrentTunnelDataMessage (); - size += s; - fragmentNumber++; - } - } - else - { - // delivery instructions don't fit. Create new message - CompleteCurrentTunnelDataMessage (); - PutI2NPMsg (block); - // don't delete msg because it's taken care inside - } - } - } + // first fragment + di[0] |= 0x08; // fragmented + htobuf32(di + diLen, msgID); + diLen += 4; // Message ID + htobe16buf(di + diLen, size); + diLen += 2; // size + memcpy(m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); + memcpy(m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer(), size); + m_CurrentTunnelDataMsg->len += diLen + size; + CompleteCurrentTunnelDataMessage(); + // follow on fragments + int fragmentNumber = 1; + while (size < msg->GetLength()) { + CreateCurrentTunnelDataMessage(); + uint8_t *buf = m_CurrentTunnelDataMsg->GetBuffer(); + buf[0] = 0x80 | (fragmentNumber << 1); // frag + bool isLastFragment = false; + size_t s = msg->GetLength() - size; + if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions + s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; + else // last fragment + { + buf[0] |= 0x01; + isLastFragment = true; + } + htobuf32(buf + 1, msgID); //Message ID + htobe16buf(buf + 5, s); // size + memcpy(buf + 7, msg->GetBuffer() + size, s); + m_CurrentTunnelDataMsg->len += s + 7; + if (isLastFragment) { + if (m_RemainingSize < (s + 7)) { + LogPrint(eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", + s + 7); + } else { + m_RemainingSize -= s + 7; + if (m_RemainingSize == 0) + CompleteCurrentTunnelDataMessage(); + } + } else + CompleteCurrentTunnelDataMessage(); + size += s; + fragmentNumber++; + } + } else { + // delivery instructions don't fit. Create new message + CompleteCurrentTunnelDataMessage(); + PutI2NPMsg(block); + // don't delete msg because it's taken care inside + } + } + } - void TunnelGatewayBuffer::ClearTunnelDataMsgs () - { - m_TunnelDataMsgs.clear (); - m_CurrentTunnelDataMsg = nullptr; - } + void TunnelGatewayBuffer::ClearTunnelDataMsgs() { + m_TunnelDataMsgs.clear(); + m_CurrentTunnelDataMsg = nullptr; + } - void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () - { - m_CurrentTunnelDataMsg = nullptr; - m_CurrentTunnelDataMsg = NewI2NPTunnelMessage (true); // tunnel endpoint is at least of two tunnel messages size - // we reserve space for padding - m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; - m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; - m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; - } + void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage() { + m_CurrentTunnelDataMsg = nullptr; + m_CurrentTunnelDataMsg = NewI2NPTunnelMessage( + true); // tunnel endpoint is at least of two tunnel messages size + // we reserve space for padding + m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; + m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; + m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; + } - void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () - { - if (!m_CurrentTunnelDataMsg) return; - uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); - size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; + void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage() { + if (!m_CurrentTunnelDataMsg) return; + uint8_t *payload = m_CurrentTunnelDataMsg->GetBuffer(); + size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; - m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; - uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); - RAND_bytes (buf + 4, 16); // original IV - memcpy (payload + size, buf + 4, 16); // copy IV for checksum - uint8_t hash[32]; - SHA256(payload, size+16, hash); - memcpy (buf+20, hash, 4); // checksum - payload[-1] = 0; // zero - ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 - if (paddingSize > 0) - { - // non-zero padding - if (!m_NonZeroRandomBuffer) // first time? - { - m_NonZeroRandomBuffer = new uint8_t[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; - RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); - for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) - if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; - } - auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1); - memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); - } + m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; + uint8_t *buf = m_CurrentTunnelDataMsg->GetPayload(); + RAND_bytes(buf + 4, 16); // original IV + memcpy(payload + size, buf + 4, 16); // copy IV for checksum + uint8_t hash[32]; + SHA256(payload, size + 16, hash); + memcpy(buf + 20, hash, 4); // checksum + payload[-1] = 0; // zero + ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 + if (paddingSize > 0) { + // non-zero padding + if (!m_NonZeroRandomBuffer) // first time? + { + m_NonZeroRandomBuffer = new uint8_t[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; + RAND_bytes(m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); + for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) + if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; + } + auto randomOffset = rand() % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1); + memcpy(buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); + } - // we can't fill message header yet because encryption is required - m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); - m_CurrentTunnelDataMsg = nullptr; - } + // we can't fill message header yet because encryption is required + m_TunnelDataMsgs.push_back(m_CurrentTunnelDataMsg); + m_CurrentTunnelDataMsg = nullptr; + } - void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block) - { - if (block.data) - { - PutTunnelDataMsg (block); - SendBuffer (); - } - } + void TunnelGateway::SendTunnelDataMsg(const TunnelMessageBlock &block) { + if (block.data) { + PutTunnelDataMsg(block); + SendBuffer(); + } + } - void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block) - { - if (block.data) - m_Buffer.PutI2NPMsg (block); - } + void TunnelGateway::PutTunnelDataMsg(const TunnelMessageBlock &block) { + if (block.data) + m_Buffer.PutI2NPMsg(block); + } - void TunnelGateway::SendBuffer () - { - m_Buffer.CompleteCurrentTunnelDataMessage (); - std::vector > newTunnelMsgs; - const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs (); - for (auto& tunnelMsg : tunnelDataMsgs) - { - auto newMsg = CreateEmptyTunnelDataMsg (false); - m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); - htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); - newMsg->FillI2NPMessageHeader (eI2NPTunnelData); - newTunnelMsgs.push_back (newMsg); - m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; - } - m_Buffer.ClearTunnelDataMsgs (); - i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), newTunnelMsgs); - } -} + void TunnelGateway::SendBuffer() { + m_Buffer.CompleteCurrentTunnelDataMessage(); + std::vector > newTunnelMsgs; + const auto &tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs(); + for (auto &tunnelMsg: tunnelDataMsgs) { + auto newMsg = CreateEmptyTunnelDataMsg(false); + m_Tunnel->EncryptTunnelMsg(tunnelMsg, newMsg); + htobe32buf(newMsg->GetPayload(), m_Tunnel->GetNextTunnelID()); + newMsg->FillI2NPMessageHeader(eI2NPTunnelData); + newTunnelMsgs.push_back(newMsg); + m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; + } + m_Buffer.ClearTunnelDataMsgs(); + i2p::transport::transports.SendMessages(m_Tunnel->GetNextIdentHash(), newTunnelMsgs); + } + } } diff --git a/libi2pd/TunnelGateway.h b/libi2pd/TunnelGateway.h index 741bbe84..edd9150c 100644 --- a/libi2pd/TunnelGateway.h +++ b/libi2pd/TunnelGateway.h @@ -15,50 +15,56 @@ #include "I2NPProtocol.h" #include "TunnelBase.h" -namespace i2p -{ -namespace tunnel -{ - class TunnelGatewayBuffer - { - public: - TunnelGatewayBuffer (); - ~TunnelGatewayBuffer (); - void PutI2NPMsg (const TunnelMessageBlock& block); - const std::vector >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; - void ClearTunnelDataMsgs (); - void CompleteCurrentTunnelDataMessage (); +namespace i2p { + namespace tunnel { + class TunnelGatewayBuffer { + public: + TunnelGatewayBuffer(); - private: + ~TunnelGatewayBuffer(); - void CreateCurrentTunnelDataMessage (); + void PutI2NPMsg(const TunnelMessageBlock &block); - private: + const std::vector > & + GetTunnelDataMsgs() const { return m_TunnelDataMsgs; }; - std::vector > m_TunnelDataMsgs; - std::shared_ptr m_CurrentTunnelDataMsg; - size_t m_RemainingSize; - uint8_t * m_NonZeroRandomBuffer; - }; + void ClearTunnelDataMsgs(); - class TunnelGateway - { - public: + void CompleteCurrentTunnelDataMessage(); - TunnelGateway (TunnelBase * tunnel): - m_Tunnel (tunnel), m_NumSentBytes (0) {}; - void SendTunnelDataMsg (const TunnelMessageBlock& block); - void PutTunnelDataMsg (const TunnelMessageBlock& block); - void SendBuffer (); - size_t GetNumSentBytes () const { return m_NumSentBytes; }; + private: - private: + void CreateCurrentTunnelDataMessage(); - TunnelBase * m_Tunnel; - TunnelGatewayBuffer m_Buffer; - size_t m_NumSentBytes; - }; -} + private: + + std::vector > m_TunnelDataMsgs; + std::shared_ptr m_CurrentTunnelDataMsg; + size_t m_RemainingSize; + uint8_t *m_NonZeroRandomBuffer; + }; + + class TunnelGateway { + public: + + TunnelGateway(TunnelBase *tunnel) : + m_Tunnel(tunnel), m_NumSentBytes(0) {}; + + void SendTunnelDataMsg(const TunnelMessageBlock &block); + + void PutTunnelDataMsg(const TunnelMessageBlock &block); + + void SendBuffer(); + + size_t GetNumSentBytes() const { return m_NumSentBytes; }; + + private: + + TunnelBase *m_Tunnel; + TunnelGatewayBuffer m_Buffer; + size_t m_NumSentBytes; + }; + } } #endif diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 204ac294..000e967b 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -20,753 +20,667 @@ #include "TunnelPool.h" #include "Destination.h" -namespace i2p -{ -namespace tunnel -{ - void Path::Add (std::shared_ptr r) - { - if (r) - { - peers.push_back (r->GetRouterIdentity ()); - if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION || - r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - isShort = false; - } - } +namespace i2p { + namespace tunnel { + void Path::Add(std::shared_ptr r) { + if (r) { + peers.push_back(r->GetRouterIdentity()); + if (r->GetVersion() < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION || + r->GetRouterIdentity()->GetCryptoKeyType() != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + isShort = false; + } + } - void Path::Reverse () - { - std::reverse (peers.begin (), peers.end ()); - } + void Path::Reverse() { + std::reverse(peers.begin(), peers.end()); + } - TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, - int numOutboundTunnels, int inboundVariance, int outboundVariance): - m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), - m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), - m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance), - m_IsActive (true), m_CustomPeerSelector(nullptr) - { - if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) - m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY; - if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY) - m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY; - if (m_InboundVariance < 0 && m_NumInboundHops + m_InboundVariance <= 0) - m_InboundVariance = m_NumInboundHops ? -m_NumInboundHops + 1 : 0; - if (m_OutboundVariance < 0 && m_NumOutboundHops + m_OutboundVariance <= 0) - m_OutboundVariance = m_NumOutboundHops ? -m_NumOutboundHops + 1 : 0; - if (m_InboundVariance > 0 && m_NumInboundHops + m_InboundVariance > STANDARD_NUM_RECORDS) - m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0; - if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS) - m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0; - m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL; - } + TunnelPool::TunnelPool(int numInboundHops, int numOutboundHops, int numInboundTunnels, + int numOutboundTunnels, int inboundVariance, int outboundVariance) : + m_NumInboundHops(numInboundHops), m_NumOutboundHops(numOutboundHops), + m_NumInboundTunnels(numInboundTunnels), m_NumOutboundTunnels(numOutboundTunnels), + m_InboundVariance(inboundVariance), m_OutboundVariance(outboundVariance), + m_IsActive(true), m_CustomPeerSelector(nullptr) { + if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) + m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY; + if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY) + m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY; + if (m_InboundVariance < 0 && m_NumInboundHops + m_InboundVariance <= 0) + m_InboundVariance = m_NumInboundHops ? -m_NumInboundHops + 1 : 0; + if (m_OutboundVariance < 0 && m_NumOutboundHops + m_OutboundVariance <= 0) + m_OutboundVariance = m_NumOutboundHops ? -m_NumOutboundHops + 1 : 0; + if (m_InboundVariance > 0 && m_NumInboundHops + m_InboundVariance > STANDARD_NUM_RECORDS) + m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops + : 0; + if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS) + m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - + m_NumOutboundHops : 0; + m_NextManageTime = i2p::util::GetSecondsSinceEpoch() + rand() % TUNNEL_POOL_MANAGE_INTERVAL; + } - TunnelPool::~TunnelPool () - { - DetachTunnels (); - } + TunnelPool::~TunnelPool() { + DetachTunnels(); + } - void TunnelPool::SetExplicitPeers (std::shared_ptr > explicitPeers) - { - m_ExplicitPeers = explicitPeers; - if (m_ExplicitPeers) - { - int size = m_ExplicitPeers->size (); - if (m_NumInboundHops > size) - { - m_NumInboundHops = size; - LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers"); - } - if (m_NumOutboundHops > size) - { - m_NumOutboundHops = size; - LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers"); - } - m_NumInboundTunnels = 1; - m_NumOutboundTunnels = 1; - } - } + void TunnelPool::SetExplicitPeers(std::shared_ptr > explicitPeers) { + m_ExplicitPeers = explicitPeers; + if (m_ExplicitPeers) { + int size = m_ExplicitPeers->size(); + if (m_NumInboundHops > size) { + m_NumInboundHops = size; + LogPrint(eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, + " for explicit peers"); + } + if (m_NumOutboundHops > size) { + m_NumOutboundHops = size; + LogPrint(eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, + " for explicit peers"); + } + m_NumInboundTunnels = 1; + m_NumOutboundTunnels = 1; + } + } - void TunnelPool::DetachTunnels () - { - { - std::unique_lock l(m_InboundTunnelsMutex); - for (auto& it: m_InboundTunnels) - it->SetTunnelPool (nullptr); - m_InboundTunnels.clear (); - } - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (auto& it: m_OutboundTunnels) - it->SetTunnelPool (nullptr); - m_OutboundTunnels.clear (); - } - m_Tests.clear (); - } + void TunnelPool::DetachTunnels() { + { + std::unique_lock l(m_InboundTunnelsMutex); + for (auto &it: m_InboundTunnels) + it->SetTunnelPool(nullptr); + m_InboundTunnels.clear(); + } + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto &it: m_OutboundTunnels) + it->SetTunnelPool(nullptr); + m_OutboundTunnels.clear(); + } + m_Tests.clear(); + } - bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) - { - if( inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0) - { - m_NumInboundHops = inHops; - m_NumOutboundHops = outHops; - m_NumInboundTunnels = inQuant; - m_NumOutboundTunnels = outQuant; - return true; - } - return false; - } + bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) { + if (inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0) { + m_NumInboundHops = inHops; + m_NumOutboundHops = outHops; + m_NumInboundTunnels = inQuant; + m_NumOutboundTunnels = outQuant; + return true; + } + return false; + } - void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) - { - if (!m_IsActive) return; - { - std::unique_lock l(m_InboundTunnelsMutex); - if (createdTunnel->IsRecreated ()) - { - // find and mark old tunnel as expired - createdTunnel->SetRecreated (false); - for (auto& it: m_InboundTunnels) - if (it->IsRecreated () && it->GetNextIdentHash () == createdTunnel->GetNextIdentHash ()) - { - it->SetState (eTunnelStateExpiring); - break; - } - } - m_InboundTunnels.insert (createdTunnel); - } - if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); - } + void TunnelPool::TunnelCreated(std::shared_ptr createdTunnel) { + if (!m_IsActive) return; + { + std::unique_lock l(m_InboundTunnelsMutex); + if (createdTunnel->IsRecreated()) { + // find and mark old tunnel as expired + createdTunnel->SetRecreated(false); + for (auto &it: m_InboundTunnels) + if (it->IsRecreated() && it->GetNextIdentHash() == createdTunnel->GetNextIdentHash()) { + it->SetState(eTunnelStateExpiring); + break; + } + } + m_InboundTunnels.insert(createdTunnel); + } + if (m_LocalDestination) + m_LocalDestination->SetLeaseSetUpdated(); + } - void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) - { - if (expiredTunnel) - { - expiredTunnel->SetTunnelPool (nullptr); - for (auto& it: m_Tests) - if (it.second.second == expiredTunnel) it.second.second = nullptr; + void TunnelPool::TunnelExpired(std::shared_ptr expiredTunnel) { + if (expiredTunnel) { + expiredTunnel->SetTunnelPool(nullptr); + for (auto &it: m_Tests) + if (it.second.second == expiredTunnel) it.second.second = nullptr; - std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.erase (expiredTunnel); - } - } + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.erase(expiredTunnel); + } + } - void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) - { - if (!m_IsActive) return; - { - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.insert (createdTunnel); - } - } + void TunnelPool::TunnelCreated(std::shared_ptr createdTunnel) { + if (!m_IsActive) return; + { + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.insert(createdTunnel); + } + } - void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) - { - if (expiredTunnel) - { - expiredTunnel->SetTunnelPool (nullptr); - for (auto& it: m_Tests) - if (it.second.first == expiredTunnel) it.second.first = nullptr; + void TunnelPool::TunnelExpired(std::shared_ptr expiredTunnel) { + if (expiredTunnel) { + expiredTunnel->SetTunnelPool(nullptr); + for (auto &it: m_Tests) + if (it.second.first == expiredTunnel) it.second.first = nullptr; - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.erase (expiredTunnel); - } - } + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.erase(expiredTunnel); + } + } - std::vector > TunnelPool::GetInboundTunnels (int num) const - { - std::vector > v; - int i = 0; - std::shared_ptr slowTunnel; - std::unique_lock l(m_InboundTunnelsMutex); - for (const auto& it : m_InboundTunnels) - { - if (i >= num) break; - if (it->IsEstablished ()) - { - if (it->IsSlow () && !slowTunnel) - slowTunnel = it; - else - { - v.push_back (it); - i++; - } - } - } - if (slowTunnel && (int)v.size () < (num/2+1)) - v.push_back (slowTunnel); - return v; - } + std::vector > TunnelPool::GetInboundTunnels(int num) const { + std::vector > v; + int i = 0; + std::shared_ptr slowTunnel; + std::unique_lock l(m_InboundTunnelsMutex); + for (const auto &it: m_InboundTunnels) { + if (i >= num) break; + if (it->IsEstablished()) { + if (it->IsSlow() && !slowTunnel) + slowTunnel = it; + else { + v.push_back(it); + i++; + } + } + } + if (slowTunnel && (int) v.size() < (num / 2 + 1)) + v.push_back(slowTunnel); + return v; + } - std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded, - i2p::data::RouterInfo::CompatibleTransports compatible) const - { - std::unique_lock l(m_OutboundTunnelsMutex); - return GetNextTunnel (m_OutboundTunnels, excluded, compatible); - } + std::shared_ptr TunnelPool::GetNextOutboundTunnel(std::shared_ptr excluded, + i2p::data::RouterInfo::CompatibleTransports compatible) const { + std::unique_lock l(m_OutboundTunnelsMutex); + return GetNextTunnel(m_OutboundTunnels, excluded, compatible); + } - std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded, - i2p::data::RouterInfo::CompatibleTransports compatible) const - { - std::unique_lock l(m_InboundTunnelsMutex); - return GetNextTunnel (m_InboundTunnels, excluded, compatible); - } + std::shared_ptr TunnelPool::GetNextInboundTunnel(std::shared_ptr excluded, + i2p::data::RouterInfo::CompatibleTransports compatible) const { + std::unique_lock l(m_InboundTunnelsMutex); + return GetNextTunnel(m_InboundTunnels, excluded, compatible); + } - template - typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, - typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const - { - if (tunnels.empty ()) return nullptr; - uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0; - bool skipped = false; - typename TTunnels::value_type tunnel = nullptr; - for (const auto& it: tunnels) - { - if (it->IsEstablished () && it != excluded && (compatible & it->GetFarEndTransports ())) - { - if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() && - !it->LatencyFitsRange(m_MinLatency, m_MaxLatency))) - { - i++; skipped = true; - continue; - } - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - if (!tunnel && skipped) - { - ind = rand () % (tunnels.size ()/2 + 1), i = 0; - for (const auto& it: tunnels) - { - if (it->IsEstablished () && it != excluded) - { - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - } - if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded; - return tunnel; - } + template + typename TTunnels::value_type TunnelPool::GetNextTunnel(TTunnels &tunnels, + typename TTunnels::value_type excluded, + i2p::data::RouterInfo::CompatibleTransports compatible) const { + if (tunnels.empty()) return nullptr; + uint32_t ind = rand() % (tunnels.size() / 2 + 1), i = 0; + bool skipped = false; + typename TTunnels::value_type tunnel = nullptr; + for (const auto &it: tunnels) { + if (it->IsEstablished() && it != excluded && (compatible & it->GetFarEndTransports())) { + if (it->IsSlow() || (HasLatencyRequirement() && it->LatencyIsKnown() && + !it->LatencyFitsRange(m_MinLatency, m_MaxLatency))) { + i++; + skipped = true; + continue; + } + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + if (!tunnel && skipped) { + ind = rand() % (tunnels.size() / 2 + 1), i = 0; + for (const auto &it: tunnels) { + if (it->IsEstablished() && it != excluded) { + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + } + if (!tunnel && excluded && excluded->IsEstablished()) tunnel = excluded; + return tunnel; + } - std::shared_ptr TunnelPool::GetNewOutboundTunnel (std::shared_ptr old) const - { - if (old && old->IsEstablished ()) return old; - std::shared_ptr tunnel; - if (old) - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (const auto& it: m_OutboundTunnels) - if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ()) - { - tunnel = it; - break; - } - } + std::shared_ptr TunnelPool::GetNewOutboundTunnel(std::shared_ptr old) const { + if (old && old->IsEstablished()) return old; + std::shared_ptr tunnel; + if (old) { + std::unique_lock l(m_OutboundTunnelsMutex); + for (const auto &it: m_OutboundTunnels) + if (it->IsEstablished() && old->GetEndpointIdentHash() == it->GetEndpointIdentHash()) { + tunnel = it; + break; + } + } - if (!tunnel) - tunnel = GetNextOutboundTunnel (); - return tunnel; - } + if (!tunnel) + tunnel = GetNextOutboundTunnel(); + return tunnel; + } - void TunnelPool::CreateTunnels () - { - int num = 0; - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (const auto& it : m_OutboundTunnels) - if (it->IsEstablished ()) num++; - } - for (int i = num; i < m_NumOutboundTunnels; i++) - CreateOutboundTunnel (); + void TunnelPool::CreateTunnels() { + int num = 0; + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (const auto &it: m_OutboundTunnels) + if (it->IsEstablished()) num++; + } + for (int i = num; i < m_NumOutboundTunnels; i++) + CreateOutboundTunnel(); - num = 0; - { - std::unique_lock l(m_InboundTunnelsMutex); - for (const auto& it : m_InboundTunnels) - if (it->IsEstablished ()) num++; - } - if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0) - { - for (auto it: m_OutboundTunnels) - { - CreatePairedInboundTunnel (it); - num++; - if (num >= m_NumInboundTunnels) break; - } - } - for (int i = num; i < m_NumInboundTunnels; i++) - CreateInboundTunnel (); + num = 0; + { + std::unique_lock l(m_InboundTunnelsMutex); + for (const auto &it: m_InboundTunnels) + if (it->IsEstablished()) num++; + } + if (!num && !m_OutboundTunnels.empty() && m_NumOutboundHops > 0) { + for (auto it: m_OutboundTunnels) { + CreatePairedInboundTunnel(it); + num++; + if (num >= m_NumInboundTunnels) break; + } + } + for (int i = num; i < m_NumInboundTunnels; i++) + CreateInboundTunnel(); - if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB - m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately - } + if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB + m_LocalDestination->SetLeaseSetUpdated(); // update LeaseSet immediately + } - void TunnelPool::TestTunnels () - { - decltype(m_Tests) tests; - { - std::unique_lock l(m_TestsMutex); - tests.swap(m_Tests); - } + void TunnelPool::TestTunnels() { + decltype(m_Tests) + tests; + { + std::unique_lock l(m_TestsMutex); + tests.swap(m_Tests); + } - for (auto& it: tests) - { - LogPrint (eLogWarning, "Tunnels: Test of tunnel ", it.first, " failed"); - // if test failed again with another tunnel we consider it failed - if (it.second.first) - { - if (it.second.first->GetState () == eTunnelStateTestFailed) - { - it.second.first->SetState (eTunnelStateFailed); - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.erase (it.second.first); - } - else - it.second.first->SetState (eTunnelStateTestFailed); - } - if (it.second.second) - { - if (it.second.second->GetState () == eTunnelStateTestFailed) - { - it.second.second->SetState (eTunnelStateFailed); - { - std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.erase (it.second.second); - } - if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); - } - else - it.second.second->SetState (eTunnelStateTestFailed); - } - } + for (auto &it: tests) { + LogPrint(eLogWarning, "Tunnels: Test of tunnel ", it.first, " failed"); + // if test failed again with another tunnel we consider it failed + if (it.second.first) { + if (it.second.first->GetState() == eTunnelStateTestFailed) { + it.second.first->SetState(eTunnelStateFailed); + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.erase(it.second.first); + } else + it.second.first->SetState(eTunnelStateTestFailed); + } + if (it.second.second) { + if (it.second.second->GetState() == eTunnelStateTestFailed) { + it.second.second->SetState(eTunnelStateFailed); + { + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.erase(it.second.second); + } + if (m_LocalDestination) + m_LocalDestination->SetLeaseSetUpdated(); + } else + it.second.second->SetState(eTunnelStateTestFailed); + } + } - // new tests - std::unique_lock l1(m_OutboundTunnelsMutex); - auto it1 = m_OutboundTunnels.begin (); - std::unique_lock l2(m_InboundTunnelsMutex); - auto it2 = m_InboundTunnels.begin (); - while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) - { - bool failed = false; - if ((*it1)->IsFailed ()) - { - failed = true; - ++it1; - } - if ((*it2)->IsFailed ()) - { - failed = true; - ++it2; - } - if (!failed) - { - uint32_t msgID; - RAND_bytes ((uint8_t *)&msgID, 4); - { - std::unique_lock l(m_TestsMutex); - m_Tests[msgID] = std::make_pair (*it1, *it2); - } - (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), - CreateDeliveryStatusMsg (msgID)); - ++it1; ++it2; - } - } - } + // new tests + std::unique_lock l1(m_OutboundTunnelsMutex); + auto it1 = m_OutboundTunnels.begin(); + std::unique_lock l2(m_InboundTunnelsMutex); + auto it2 = m_InboundTunnels.begin(); + while (it1 != m_OutboundTunnels.end() && it2 != m_InboundTunnels.end()) { + bool failed = false; + if ((*it1)->IsFailed()) { + failed = true; + ++it1; + } + if ((*it2)->IsFailed()) { + failed = true; + ++it2; + } + if (!failed) { + uint32_t msgID; + RAND_bytes((uint8_t * ) & msgID, 4); + { + std::unique_lock l(m_TestsMutex); + m_Tests[msgID] = std::make_pair(*it1, *it2); + } + (*it1)->SendTunnelDataMsg((*it2)->GetNextIdentHash(), (*it2)->GetNextTunnelID(), + CreateDeliveryStatusMsg(msgID)); + ++it1; + ++it2; + } + } + } - void TunnelPool::ManageTunnels (uint64_t ts) - { - if (ts > m_NextManageTime || ts + 2*TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted - { - CreateTunnels (); - TestTunnels (); - m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2; - } - } + void TunnelPool::ManageTunnels(uint64_t ts) { + if (ts > m_NextManageTime || + ts + 2 * TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted + { + CreateTunnels(); + TestTunnels(); + m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand() % TUNNEL_POOL_MANAGE_INTERVAL) / 2; + } + } - void TunnelPool::ProcessGarlicMessage (std::shared_ptr msg) - { - if (m_LocalDestination) - m_LocalDestination->ProcessGarlicMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } + void TunnelPool::ProcessGarlicMessage(std::shared_ptr msg) { + if (m_LocalDestination) + m_LocalDestination->ProcessGarlicMessage(msg); + else + LogPrint(eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); + } - void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - uint32_t msgID = bufbe32toh (buf); - buf += 4; - uint64_t timestamp = bufbe64toh (buf); + void TunnelPool::ProcessDeliveryStatus(std::shared_ptr msg) { + const uint8_t *buf = msg->GetPayload(); + uint32_t msgID = bufbe32toh(buf); + buf += 4; + uint64_t timestamp = bufbe64toh(buf); - decltype(m_Tests)::mapped_type test; - bool found = false; - { - std::unique_lock l(m_TestsMutex); - auto it = m_Tests.find (msgID); - if (it != m_Tests.end ()) - { - found = true; - test = it->second; - m_Tests.erase (it); - } - } - if (found) - { - uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp; - LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " milliseconds"); - int numHops = 0; - if (test.first) numHops += test.first->GetNumHops (); - if (test.second) numHops += test.second->GetNumHops (); - // restore from test failed state if any - if (test.first) - { - if (test.first->GetState () == eTunnelStateTestFailed) - test.first->SetState (eTunnelStateEstablished); - // update latency - uint64_t latency = 0; - if (numHops) latency = dlt*test.first->GetNumHops ()/numHops; - if (!latency) latency = dlt/2; - test.first->AddLatencySample(latency); - } - if (test.second) - { - if (test.second->GetState () == eTunnelStateTestFailed) - test.second->SetState (eTunnelStateEstablished); - // update latency - uint64_t latency = 0; - if (numHops) latency = dlt*test.second->GetNumHops ()/numHops; - if (!latency) latency = dlt/2; - test.second->AddLatencySample(latency); - } - } - else - { - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } - } + decltype(m_Tests) + ::mapped_type test; + bool found = false; + { + std::unique_lock l(m_TestsMutex); + auto it = m_Tests.find(msgID); + if (it != m_Tests.end()) { + found = true; + test = it->second; + m_Tests.erase(it); + } + } + if (found) { + uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch() - timestamp; + LogPrint(eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " milliseconds"); + int numHops = 0; + if (test.first) numHops += test.first->GetNumHops(); + if (test.second) numHops += test.second->GetNumHops(); + // restore from test failed state if any + if (test.first) { + if (test.first->GetState() == eTunnelStateTestFailed) + test.first->SetState(eTunnelStateEstablished); + // update latency + uint64_t latency = 0; + if (numHops) latency = dlt * test.first->GetNumHops() / numHops; + if (!latency) latency = dlt / 2; + test.first->AddLatencySample(latency); + } + if (test.second) { + if (test.second->GetState() == eTunnelStateTestFailed) + test.second->SetState(eTunnelStateEstablished); + // update latency + uint64_t latency = 0; + if (numHops) latency = dlt * test.second->GetNumHops() / numHops; + if (!latency) latency = dlt / 2; + test.second->AddLatencySample(latency); + } + } else { + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage(msg); + else + LogPrint(eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); + } + } - bool TunnelPool::IsExploratory () const - { - return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); - } + bool TunnelPool::IsExploratory() const { + return i2p::tunnel::tunnels.GetExploratoryPool() == shared_from_this(); + } - std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse) const - { - auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse): - i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse); + std::shared_ptr + TunnelPool::SelectNextHop(std::shared_ptr prevHop, bool reverse) const { + auto hop = IsExploratory() ? i2p::data::netdb.GetRandomRouter(prevHop, reverse) : + i2p::data::netdb.GetHighBandwidthRandomRouter(prevHop, reverse); - if (!hop || hop->GetProfile ()->IsBad ()) - hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse); - return hop; - } + if (!hop || hop->GetProfile()->IsBad()) + hop = i2p::data::netdb.GetRandomRouter(prevHop, reverse); + return hop; + } - bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop) - { - int start = 0; - std::shared_ptr prevHop = i2p::context.GetSharedRouterInfo (); - if(i2p::transport::transports.RoutesRestricted()) - { - /** if routes are restricted prepend trusted first hop */ - auto hop = i2p::transport::transports.GetRestrictedPeer(); - if(!hop) return false; - path.Add (hop); - prevHop = hop; - start++; - } - else if (i2p::transport::transports.GetNumPeers () > 100 || - (inbound && i2p::transport::transports.GetNumPeers () > 25)) - { - auto r = i2p::transport::transports.GetRandomPeer (); - if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && - (numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable - { - prevHop = r; - path.Add (r); - start++; - } - } + bool StandardSelectPeers(Path &path, int numHops, bool inbound, SelectHopFunc nextHop) { + int start = 0; + std::shared_ptr prevHop = i2p::context.GetSharedRouterInfo(); + if (i2p::transport::transports.RoutesRestricted()) { + /** if routes are restricted prepend trusted first hop */ + auto hop = i2p::transport::transports.GetRestrictedPeer(); + if (!hop) return false; + path.Add(hop); + prevHop = hop; + start++; + } else if (i2p::transport::transports.GetNumPeers() > 100 || + (inbound && i2p::transport::transports.GetNumPeers() > 25)) { + auto r = i2p::transport::transports.GetRandomPeer(); + if (r && r->IsECIES() && !r->GetProfile()->IsBad() && + (numHops > 1 || (r->IsV4() && (!inbound || r->IsReachable())))) // first inbound must be reachable + { + prevHop = r; + path.Add(r); + start++; + } + } - for(int i = start; i < numHops; i++ ) - { - auto hop = nextHop (prevHop, inbound); - if (!hop && !i) // if no suitable peer found for first hop, try already connected - { - LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); - hop = i2p::transport::transports.GetRandomPeer (); - if (hop && !hop->IsECIES ()) hop = nullptr; - } - if (!hop) - { - LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); - return false; - } - if ((i == numHops - 1) && (!hop->IsV4 () || // doesn't support ipv4 - (inbound && !hop->IsReachable ()))) // IBGW is not reachable - { - auto hop1 = nextHop (prevHop, true); - if (hop1) hop = hop1; - } - prevHop = hop; - path.Add (hop); - } - path.farEndTransports = prevHop->GetCompatibleTransports (inbound); // last hop - return true; - } + for (int i = start; i < numHops; i++) { + auto hop = nextHop(prevHop, inbound); + if (!hop && !i) // if no suitable peer found for first hop, try already connected + { + LogPrint(eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); + hop = i2p::transport::transports.GetRandomPeer(); + if (hop && !hop->IsECIES()) hop = nullptr; + } + if (!hop) { + LogPrint(eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64()); + return false; + } + if ((i == numHops - 1) && (!hop->IsV4() || // doesn't support ipv4 + (inbound && !hop->IsReachable()))) // IBGW is not reachable + { + auto hop1 = nextHop(prevHop, true); + if (hop1) hop = hop1; + } + prevHop = hop; + path.Add(hop); + } + path.farEndTransports = prevHop->GetCompatibleTransports(inbound); // last hop + return true; + } - bool TunnelPool::SelectPeers (Path& path, bool isInbound) - { - // explicit peers in use - if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound); - // calculate num hops - int numHops; - if (isInbound) - { - numHops = m_NumInboundHops; - if (m_InboundVariance) - { - int offset = rand () % (std::abs (m_InboundVariance) + 1); - if (m_InboundVariance < 0) offset = -offset; - numHops += offset; - } - } - else - { - numHops = m_NumOutboundHops; - if (m_OutboundVariance) - { - int offset = rand () % (std::abs (m_OutboundVariance) + 1); - if (m_OutboundVariance < 0) offset = -offset; - numHops += offset; - } - } - // peers is empty - if (numHops <= 0) return true; - // custom peer selector in use ? - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - if (m_CustomPeerSelector) - return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); - } - return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); - } + bool TunnelPool::SelectPeers(Path &path, bool isInbound) { + // explicit peers in use + if (m_ExplicitPeers) return SelectExplicitPeers(path, isInbound); + // calculate num hops + int numHops; + if (isInbound) { + numHops = m_NumInboundHops; + if (m_InboundVariance) { + int offset = rand() % (std::abs(m_InboundVariance) + 1); + if (m_InboundVariance < 0) offset = -offset; + numHops += offset; + } + } else { + numHops = m_NumOutboundHops; + if (m_OutboundVariance) { + int offset = rand() % (std::abs(m_OutboundVariance) + 1); + if (m_OutboundVariance < 0) offset = -offset; + numHops += offset; + } + } + // peers is empty + if (numHops <= 0) return true; + // custom peer selector in use ? + { + std::lock_guard lock(m_CustomPeerSelectorMutex); + if (m_CustomPeerSelector) + return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); + } + return StandardSelectPeers(path, numHops, isInbound, + std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, + std::placeholders::_2)); + } - bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound) - { - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; - if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size (); - if (!numHops) return false; - for (int i = 0; i < numHops; i++) - { - auto& ident = (*m_ExplicitPeers)[i]; - auto r = i2p::data::netdb.FindRouter (ident); - if (r) - { - if (r->IsECIES ()) - { - path.Add (r); - if (i == numHops - 1) - path.farEndTransports = r->GetCompatibleTransports (isInbound); - } - else - { - LogPrint (eLogError, "Tunnels: ElGamal router ", ident.ToBase64 (), " is not supported"); - return false; - } - } - else - { - LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ()); - i2p::data::netdb.RequestDestination (ident); - return false; - } - } - return true; - } + bool TunnelPool::SelectExplicitPeers(Path &path, bool isInbound) { + int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + if (numHops > (int) m_ExplicitPeers->size()) numHops = m_ExplicitPeers->size(); + if (!numHops) return false; + for (int i = 0; i < numHops; i++) { + auto &ident = (*m_ExplicitPeers)[i]; + auto r = i2p::data::netdb.FindRouter(ident); + if (r) { + if (r->IsECIES()) { + path.Add(r); + if (i == numHops - 1) + path.farEndTransports = r->GetCompatibleTransports(isInbound); + } else { + LogPrint(eLogError, "Tunnels: ElGamal router ", ident.ToBase64(), " is not supported"); + return false; + } + } else { + LogPrint(eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64()); + i2p::data::netdb.RequestDestination(ident); + return false; + } + } + return true; + } - void TunnelPool::CreateInboundTunnel () - { - LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel..."); - Path path; - if (SelectPeers (path, true)) - { - auto outboundTunnel = GetNextOutboundTunnel (nullptr, path.farEndTransports); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); - std::shared_ptr config; - if (m_NumInboundHops > 0) - { - path.Reverse (); - config = std::make_shared (path.peers, path.isShort, path.farEndTransports); - } - auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel); - if (tunnel->IsEstablished ()) // zero hops - TunnelCreated (tunnel); - } - else - LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available"); - } + void TunnelPool::CreateInboundTunnel() { + LogPrint(eLogDebug, "Tunnels: Creating destination inbound tunnel..."); + Path path; + if (SelectPeers(path, true)) { + auto outboundTunnel = GetNextOutboundTunnel(nullptr, path.farEndTransports); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel(); + std::shared_ptr config; + if (m_NumInboundHops > 0) { + path.Reverse(); + config = std::make_shared(path.peers, path.isShort, path.farEndTransports); + } + auto tunnel = tunnels.CreateInboundTunnel(config, shared_from_this(), outboundTunnel); + if (tunnel->IsEstablished()) // zero hops + TunnelCreated(tunnel); + } else + LogPrint(eLogError, "Tunnels: Can't create inbound tunnel, no peers available"); + } - void TunnelPool::RecreateInboundTunnel (std::shared_ptr tunnel) - { - if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow - { - CreateInboundTunnel (); - return; - } - auto outboundTunnel = GetNextOutboundTunnel (nullptr, tunnel->GetFarEndTransports ()); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); - LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel..."); - std::shared_ptr config; - if (m_NumInboundHops > 0 && tunnel->GetPeers().size()) - config = std::make_shared(tunnel->GetPeers (), tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); - if (!m_NumInboundHops || config) - { - auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); - if (newTunnel->IsEstablished ()) // zero hops - TunnelCreated (newTunnel); - else - newTunnel->SetRecreated (true); - } - } + void TunnelPool::RecreateInboundTunnel(std::shared_ptr tunnel) { + if (IsExploratory() || tunnel->IsSlow()) // always create new exploratory tunnel or if slow + { + CreateInboundTunnel(); + return; + } + auto outboundTunnel = GetNextOutboundTunnel(nullptr, tunnel->GetFarEndTransports()); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel(); + LogPrint(eLogDebug, "Tunnels: Re-creating destination inbound tunnel..."); + std::shared_ptr config; + if (m_NumInboundHops > 0 && tunnel->GetPeers().size()) + config = std::make_shared(tunnel->GetPeers(), tunnel->IsShortBuildMessage(), + tunnel->GetFarEndTransports()); + if (!m_NumInboundHops || config) { + auto newTunnel = tunnels.CreateInboundTunnel(config, shared_from_this(), outboundTunnel); + if (newTunnel->IsEstablished()) // zero hops + TunnelCreated(newTunnel); + else + newTunnel->SetRecreated(true); + } + } - void TunnelPool::CreateOutboundTunnel () - { - LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); - Path path; - if (SelectPeers (path, false)) - { - auto inboundTunnel = GetNextInboundTunnel (nullptr, path.farEndTransports); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (!inboundTunnel) - { - LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found"); - return; - } + void TunnelPool::CreateOutboundTunnel() { + LogPrint(eLogDebug, "Tunnels: Creating destination outbound tunnel..."); + Path path; + if (SelectPeers(path, false)) { + auto inboundTunnel = GetNextInboundTunnel(nullptr, path.farEndTransports); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel(); + if (!inboundTunnel) { + LogPrint(eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found"); + return; + } - if (m_LocalDestination && !m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - path.isShort = false; // because can't handle ECIES encrypted reply + if (m_LocalDestination && + !m_LocalDestination->SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) + path.isShort = false; // because can't handle ECIES encrypted reply - std::shared_ptr config; - if (m_NumOutboundHops > 0) - config = std::make_shared(path.peers, inboundTunnel->GetNextTunnelID (), - inboundTunnel->GetNextIdentHash (), path.isShort, path.farEndTransports); + std::shared_ptr config; + if (m_NumOutboundHops > 0) + config = std::make_shared(path.peers, inboundTunnel->GetNextTunnelID(), + inboundTunnel->GetNextIdentHash(), path.isShort, + path.farEndTransports); - std::shared_ptr tunnel; - if (path.isShort) - { - // TODO: implement it better - tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ()); - tunnel->SetTunnelPool (shared_from_this ()); - } - else - tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); - if (tunnel && tunnel->IsEstablished ()) // zero hops - TunnelCreated (tunnel); - } - else - LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); - } + std::shared_ptr tunnel; + if (path.isShort) { + // TODO: implement it better + tunnel = tunnels.CreateOutboundTunnel(config, inboundTunnel->GetTunnelPool()); + tunnel->SetTunnelPool(shared_from_this()); + } else + tunnel = tunnels.CreateOutboundTunnel(config, shared_from_this()); + if (tunnel && tunnel->IsEstablished()) // zero hops + TunnelCreated(tunnel); + } else + LogPrint(eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); + } - void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) - { - if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow - { - CreateOutboundTunnel (); - return; - } - auto inboundTunnel = GetNextInboundTunnel (nullptr, tunnel->GetFarEndTransports ()); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (inboundTunnel) - { - LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); - std::shared_ptr config; - if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) - { - config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), - inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); - } - if (!m_NumOutboundHops || config) - { - auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); - if (newTunnel->IsEstablished ()) // zero hops - TunnelCreated (newTunnel); - } - } - else - LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found"); - } + void TunnelPool::RecreateOutboundTunnel(std::shared_ptr tunnel) { + if (IsExploratory() || tunnel->IsSlow()) // always create new exploratory tunnel or if slow + { + CreateOutboundTunnel(); + return; + } + auto inboundTunnel = GetNextInboundTunnel(nullptr, tunnel->GetFarEndTransports()); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel(); + if (inboundTunnel) { + LogPrint(eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); + std::shared_ptr config; + if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) { + config = std::make_shared(tunnel->GetPeers(), inboundTunnel->GetNextTunnelID(), + inboundTunnel->GetNextIdentHash(), + inboundTunnel->IsShortBuildMessage(), + tunnel->GetFarEndTransports()); + } + if (!m_NumOutboundHops || config) { + auto newTunnel = tunnels.CreateOutboundTunnel(config, shared_from_this()); + if (newTunnel->IsEstablished()) // zero hops + TunnelCreated(newTunnel); + } + } else + LogPrint(eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found"); + } - void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr outboundTunnel) - { - LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); - auto tunnel = tunnels.CreateInboundTunnel ( - m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers (), - outboundTunnel->IsShortBuildMessage ()) : nullptr, - shared_from_this (), outboundTunnel); - if (tunnel->IsEstablished ()) // zero hops - TunnelCreated (tunnel); - } + void TunnelPool::CreatePairedInboundTunnel(std::shared_ptr outboundTunnel) { + LogPrint(eLogDebug, "Tunnels: Creating paired inbound tunnel..."); + auto tunnel = tunnels.CreateInboundTunnel( + m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers(), + outboundTunnel->IsShortBuildMessage()) + : nullptr, + shared_from_this(), outboundTunnel); + if (tunnel->IsEstablished()) // zero hops + TunnelCreated(tunnel); + } - void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector) - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - m_CustomPeerSelector = selector; - } + void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector *selector) { + std::lock_guard lock(m_CustomPeerSelectorMutex); + m_CustomPeerSelector = selector; + } - void TunnelPool::UnsetCustomPeerSelector() - { - SetCustomPeerSelector(nullptr); - } + void TunnelPool::UnsetCustomPeerSelector() { + SetCustomPeerSelector(nullptr); + } - bool TunnelPool::HasCustomPeerSelector() - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - return m_CustomPeerSelector != nullptr; - } + bool TunnelPool::HasCustomPeerSelector() { + std::lock_guard lock(m_CustomPeerSelectorMutex); + return m_CustomPeerSelector != nullptr; + } - std::shared_ptr TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr exclude) const - { - std::shared_ptr tun = nullptr; - std::unique_lock lock(m_InboundTunnelsMutex); - uint64_t min = 1000000; - for (const auto & itr : m_InboundTunnels) { - if(!itr->LatencyIsKnown()) continue; - auto l = itr->GetMeanLatency(); - if (l >= min) continue; - tun = itr; - if(tun == exclude) continue; - min = l; - } - return tun; - } + std::shared_ptr + TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr exclude) const { + std::shared_ptr tun = nullptr; + std::unique_lock lock(m_InboundTunnelsMutex); + uint64_t min = 1000000; + for (const auto &itr: m_InboundTunnels) { + if (!itr->LatencyIsKnown()) continue; + auto l = itr->GetMeanLatency(); + if (l >= min) continue; + tun = itr; + if (tun == exclude) continue; + min = l; + } + return tun; + } - std::shared_ptr TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr exclude) const - { - std::shared_ptr tun = nullptr; - std::unique_lock lock(m_OutboundTunnelsMutex); - uint64_t min = 1000000; - for (const auto & itr : m_OutboundTunnels) { - if(!itr->LatencyIsKnown()) continue; - auto l = itr->GetMeanLatency(); - if (l >= min) continue; - tun = itr; - if(tun == exclude) continue; - min = l; - } - return tun; - } -} + std::shared_ptr + TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr exclude) const { + std::shared_ptr tun = nullptr; + std::unique_lock lock(m_OutboundTunnelsMutex); + uint64_t min = 1000000; + for (const auto &itr: m_OutboundTunnels) { + if (!itr->LatencyIsKnown()) continue; + auto l = itr->GetMeanLatency(); + if (l >= min) continue; + tun = itr; + if (tun == exclude) continue; + min = l; + } + return tun; + } + } } diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index d8c60d69..214c8918 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -23,139 +23,187 @@ #include "RouterContext.h" #include "Garlic.h" -namespace i2p -{ -namespace tunnel -{ - const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds - const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; - const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; +namespace i2p { + namespace tunnel { + const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds + const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; + const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; - class Tunnel; - class InboundTunnel; - class OutboundTunnel; + class Tunnel; - typedef std::shared_ptr Peer; - struct Path - { - std::vector peers; - bool isShort = true; - i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports; + class InboundTunnel; - void Add (std::shared_ptr r); - void Reverse (); - }; + class OutboundTunnel; - /** interface for custom tunnel peer selection algorithm */ - struct ITunnelPeerSelector - { - virtual ~ITunnelPeerSelector() {}; - virtual bool SelectPeers(Path & peers, int hops, bool isInbound) = 0; - }; + typedef std::shared_ptr Peer; + + struct Path { + std::vector peers; + bool isShort = true; + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports; + + void Add(std::shared_ptr r); + + void Reverse(); + }; + + /** interface for custom tunnel peer selection algorithm */ + struct ITunnelPeerSelector { + virtual ~ITunnelPeerSelector() {}; + + virtual bool SelectPeers(Path &peers, int hops, bool isInbound) = 0; + }; - typedef std::function(std::shared_ptr, bool)> SelectHopFunc; - bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); + typedef std::function(std::shared_ptr, + bool)> SelectHopFunc; - class TunnelPool: public std::enable_shared_from_this // per local destination - { - public: + bool StandardSelectPeers(Path &path, int numHops, bool inbound, SelectHopFunc nextHop); - TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, - int numOutboundTunnels, int inboundVariance, int outboundVariance); - ~TunnelPool (); + class TunnelPool : public std::enable_shared_from_this // per local destination + { + public: - std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; - void SetLocalDestination (std::shared_ptr destination) { m_LocalDestination = destination; }; - void SetExplicitPeers (std::shared_ptr > explicitPeers); + TunnelPool(int numInboundHops, int numOutboundHops, int numInboundTunnels, + int numOutboundTunnels, int inboundVariance, int outboundVariance); - void CreateTunnels (); - void TunnelCreated (std::shared_ptr createdTunnel); - void TunnelExpired (std::shared_ptr expiredTunnel); - void TunnelCreated (std::shared_ptr createdTunnel); - void TunnelExpired (std::shared_ptr expiredTunnel); - void RecreateInboundTunnel (std::shared_ptr tunnel); - void RecreateOutboundTunnel (std::shared_ptr tunnel); - std::vector > GetInboundTunnels (int num) const; - std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr, - i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const; - std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr, - i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const; - std::shared_ptr GetNewOutboundTunnel (std::shared_ptr old) const; - void ManageTunnels (uint64_t ts); - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatus (std::shared_ptr msg); + ~TunnelPool(); - bool IsExploratory () const; - bool IsActive () const { return m_IsActive; }; - void SetActive (bool isActive) { m_IsActive = isActive; }; - void DetachTunnels (); + std::shared_ptr GetLocalDestination() const { return m_LocalDestination; }; - int GetNumInboundTunnels () const { return m_NumInboundTunnels; }; - int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; }; - int GetNumInboundHops() const { return m_NumInboundHops; }; - int GetNumOutboundHops() const { return m_NumOutboundHops; }; + void SetLocalDestination( + std::shared_ptr destination) { m_LocalDestination = destination; }; - /** i2cp reconfigure */ - bool Reconfigure(int inboundHops, int outboundHops, int inboundQuant, int outboundQuant); + void SetExplicitPeers(std::shared_ptr > explicitPeers); - void SetCustomPeerSelector(ITunnelPeerSelector * selector); - void UnsetCustomPeerSelector(); - bool HasCustomPeerSelector(); + void CreateTunnels(); - /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ - void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; } + void TunnelCreated(std::shared_ptr createdTunnel); - /** @brief return true if this tunnel pool has a latency requirement */ - bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } + void TunnelExpired(std::shared_ptr expiredTunnel); - /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ - std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude = nullptr) const; - std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const; + void TunnelCreated(std::shared_ptr createdTunnel); - // for overriding tunnel peer selection - std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse) const; + void TunnelExpired(std::shared_ptr expiredTunnel); - private: + void RecreateInboundTunnel(std::shared_ptr tunnel); - void TestTunnels (); - void CreateInboundTunnel (); - void CreateOutboundTunnel (); - void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); - template - typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, - typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const; - bool SelectPeers (Path& path, bool isInbound); - bool SelectExplicitPeers (Path& path, bool isInbound); + void RecreateOutboundTunnel(std::shared_ptr tunnel); - private: + std::vector > GetInboundTunnels(int num) const; - std::shared_ptr m_LocalDestination; - int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels, - m_InboundVariance, m_OutboundVariance; - std::shared_ptr > m_ExplicitPeers; - mutable std::mutex m_InboundTunnelsMutex; - std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first - mutable std::mutex m_OutboundTunnelsMutex; - std::set, TunnelCreationTimeCmp> m_OutboundTunnels; - mutable std::mutex m_TestsMutex; - std::map, std::shared_ptr > > m_Tests; - bool m_IsActive; - uint64_t m_NextManageTime; // in seconds - std::mutex m_CustomPeerSelectorMutex; - ITunnelPeerSelector * m_CustomPeerSelector; + std::shared_ptr GetNextOutboundTunnel(std::shared_ptr excluded = nullptr, + i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const; - uint64_t m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms - uint64_t m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms + std::shared_ptr GetNextInboundTunnel(std::shared_ptr excluded = nullptr, + i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const; - public: + std::shared_ptr GetNewOutboundTunnel(std::shared_ptr old) const; - // for HTTP only - const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; - const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; + void ManageTunnels(uint64_t ts); - }; -} + void ProcessGarlicMessage(std::shared_ptr msg); + + void ProcessDeliveryStatus(std::shared_ptr msg); + + bool IsExploratory() const; + + bool IsActive() const { return m_IsActive; }; + + void SetActive(bool isActive) { m_IsActive = isActive; }; + + void DetachTunnels(); + + int GetNumInboundTunnels() const { return m_NumInboundTunnels; }; + + int GetNumOutboundTunnels() const { return m_NumOutboundTunnels; }; + + int GetNumInboundHops() const { return m_NumInboundHops; }; + + int GetNumOutboundHops() const { return m_NumOutboundHops; }; + + /** i2cp reconfigure */ + bool Reconfigure(int inboundHops, int outboundHops, int inboundQuant, int outboundQuant); + + void SetCustomPeerSelector(ITunnelPeerSelector *selector); + + void UnsetCustomPeerSelector(); + + bool HasCustomPeerSelector(); + + /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ + void RequireLatency(uint64_t min, uint64_t max) { + m_MinLatency = min; + m_MaxLatency = max; + } + + /** @brief return true if this tunnel pool has a latency requirement */ + bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } + + /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ + std::shared_ptr + GetLowestLatencyInboundTunnel(std::shared_ptr exclude = nullptr) const; + + std::shared_ptr + GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const; + + // for overriding tunnel peer selection + std::shared_ptr + SelectNextHop(std::shared_ptr prevHop, bool reverse) const; + + private: + + void TestTunnels(); + + void CreateInboundTunnel(); + + void CreateOutboundTunnel(); + + void CreatePairedInboundTunnel(std::shared_ptr outboundTunnel); + + template + typename TTunnels::value_type GetNextTunnel(TTunnels &tunnels, + typename TTunnels::value_type excluded, + i2p::data::RouterInfo::CompatibleTransports compatible) const; + + bool SelectPeers(Path &path, bool isInbound); + + bool SelectExplicitPeers(Path &path, bool isInbound); + + private: + + std::shared_ptr m_LocalDestination; + int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels, + m_InboundVariance, m_OutboundVariance; + std::shared_ptr > m_ExplicitPeers; + mutable std::mutex m_InboundTunnelsMutex; + std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first + mutable std::mutex m_OutboundTunnelsMutex; + std::set, TunnelCreationTimeCmp> m_OutboundTunnels; + mutable std::mutex m_TestsMutex; + std::map, std::shared_ptr > > m_Tests; + bool m_IsActive; + uint64_t m_NextManageTime; // in seconds + std::mutex m_CustomPeerSelectorMutex; + ITunnelPeerSelector *m_CustomPeerSelector; + + uint64_t m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms + uint64_t m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms + + public: + + // for HTTP only + const decltype(m_OutboundTunnels) + & + + GetOutboundTunnels() const { return m_OutboundTunnels; }; + const decltype(m_InboundTunnels) + & + + GetInboundTunnels() const { return m_InboundTunnels; }; + + }; + } } #endif diff --git a/libi2pd/api.cpp b/libi2pd/api.cpp index 905fab75..98325aae 100644 --- a/libi2pd/api.cpp +++ b/libi2pd/api.cpp @@ -20,127 +20,120 @@ #include "FS.h" #include "api.h" -namespace i2p -{ -namespace api -{ - void InitI2P (int argc, char* argv[], const char * appName) - { - i2p::config::Init (); - i2p::config::ParseCmdline (argc, argv, true); // ignore unknown options and help - i2p::config::Finalize (); +namespace i2p { + namespace api { + void InitI2P(int argc, char *argv[], const char *appName) { + i2p::config::Init(); + i2p::config::ParseCmdline(argc, argv, true); // ignore unknown options and help + i2p::config::Finalize(); - std::string datadir; i2p::config::GetOption("datadir", datadir); + std::string datadir; + i2p::config::GetOption("datadir", datadir); - i2p::fs::SetAppName (appName); - i2p::fs::DetectDataDir(datadir, false); - i2p::fs::Init(); + i2p::fs::SetAppName(appName); + i2p::fs::DetectDataDir(datadir, false); + i2p::fs::Init(); - bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); - bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); - bool avx; i2p::config::GetOption("cpuext.avx", avx); - bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); - i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt); + bool precomputation; + i2p::config::GetOption("precomputation.elgamal", precomputation); + bool aesni; + i2p::config::GetOption("cpuext.aesni", aesni); + bool avx; + i2p::config::GetOption("cpuext.avx", avx); + bool forceCpuExt; + i2p::config::GetOption("cpuext.force", forceCpuExt); + i2p::crypto::InitCrypto(precomputation, aesni, avx, forceCpuExt); - int netID; i2p::config::GetOption("netid", netID); - i2p::context.SetNetID (netID); + int netID; + i2p::config::GetOption("netid", netID); + i2p::context.SetNetID(netID); - i2p::context.Init (); - } + i2p::context.Init(); + } - void TerminateI2P () - { - i2p::crypto::TerminateCrypto (); - } + void TerminateI2P() { + i2p::crypto::TerminateCrypto(); + } - void StartI2P (std::shared_ptr logStream) - { - if (logStream) - i2p::log::Logger().SendTo (logStream); - else - i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log")); - i2p::log::Logger().Start (); - LogPrint(eLogInfo, "API: Starting NetDB"); - i2p::data::netdb.Start(); - LogPrint(eLogInfo, "API: Starting Transports"); - i2p::transport::transports.Start(); - LogPrint(eLogInfo, "API: Starting Tunnels"); - i2p::tunnel::tunnels.Start(); - } + void StartI2P(std::shared_ptr logStream) { + if (logStream) + i2p::log::Logger().SendTo(logStream); + else + i2p::log::Logger().SendTo(i2p::fs::DataDirPath(i2p::fs::GetAppName() + ".log")); + i2p::log::Logger().Start(); + LogPrint(eLogInfo, "API: Starting NetDB"); + i2p::data::netdb.Start(); + LogPrint(eLogInfo, "API: Starting Transports"); + i2p::transport::transports.Start(); + LogPrint(eLogInfo, "API: Starting Tunnels"); + i2p::tunnel::tunnels.Start(); + } - void StopI2P () - { - LogPrint(eLogInfo, "API: Shutting down"); - LogPrint(eLogInfo, "API: Stopping Tunnels"); - i2p::tunnel::tunnels.Stop(); - LogPrint(eLogInfo, "API: Stopping Transports"); - i2p::transport::transports.Stop(); - LogPrint(eLogInfo, "API: Stopping NetDB"); - i2p::data::netdb.Stop(); - i2p::log::Logger().Stop (); - } + void StopI2P() { + LogPrint(eLogInfo, "API: Shutting down"); + LogPrint(eLogInfo, "API: Stopping Tunnels"); + i2p::tunnel::tunnels.Stop(); + LogPrint(eLogInfo, "API: Stopping Transports"); + i2p::transport::transports.Stop(); + LogPrint(eLogInfo, "API: Stopping NetDB"); + i2p::data::netdb.Stop(); + i2p::log::Logger().Stop(); + } - void RunPeerTest () - { - i2p::transport::transports.PeerTest (); - } + void RunPeerTest() { + i2p::transport::transports.PeerTest(); + } - std::shared_ptr CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params) - { - auto localDestination = std::make_shared (keys, isPublic, params); - localDestination->Start (); - return localDestination; - } + std::shared_ptr + CreateLocalDestination(const i2p::data::PrivateKeys &keys, bool isPublic, + const std::map *params) { + auto localDestination = std::make_shared(keys, isPublic, params); + localDestination->Start(); + return localDestination; + } - std::shared_ptr CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); - auto localDestination = std::make_shared (keys, isPublic, params); - localDestination->Start (); - return localDestination; - } + std::shared_ptr + CreateLocalDestination(bool isPublic, i2p::data::SigningKeyType sigType, + const std::map *params) { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys(sigType); + auto localDestination = std::make_shared(keys, isPublic, params); + localDestination->Start(); + return localDestination; + } - void DestroyLocalDestination (std::shared_ptr dest) - { - if (dest) - dest->Stop (); - } + void DestroyLocalDestination(std::shared_ptr dest) { + if (dest) + dest->Stop(); + } - void RequestLeaseSet (std::shared_ptr dest, const i2p::data::IdentHash& remote) - { - if (dest) - dest->RequestDestination (remote); - } + void RequestLeaseSet(std::shared_ptr dest, const i2p::data::IdentHash &remote) { + if (dest) + dest->RequestDestination(remote); + } - std::shared_ptr CreateStream (std::shared_ptr dest, const i2p::data::IdentHash& remote) - { - if (!dest) return nullptr; - auto leaseSet = dest->FindLeaseSet (remote); - if (leaseSet) - { - auto stream = dest->CreateStream (leaseSet); - stream->Send (nullptr, 0); // connect - return stream; - } - else - { - RequestLeaseSet (dest, remote); - return nullptr; - } - } + std::shared_ptr + CreateStream(std::shared_ptr dest, const i2p::data::IdentHash &remote) { + if (!dest) return nullptr; + auto leaseSet = dest->FindLeaseSet(remote); + if (leaseSet) { + auto stream = dest->CreateStream(leaseSet); + stream->Send(nullptr, 0); // connect + return stream; + } else { + RequestLeaseSet(dest, remote); + return nullptr; + } + } - void AcceptStream (std::shared_ptr dest, const i2p::stream::StreamingDestination::Acceptor& acceptor) - { - if (dest) - dest->AcceptStreams (acceptor); - } + void AcceptStream(std::shared_ptr dest, + const i2p::stream::StreamingDestination::Acceptor &acceptor) { + if (dest) + dest->AcceptStreams(acceptor); + } - void DestroyStream (std::shared_ptr stream) - { - if (stream) - stream->Close (); - } -} + void DestroyStream(std::shared_ptr stream) { + if (stream) + stream->Close(); + } + } } diff --git a/libi2pd/api.h b/libi2pd/api.h index 9b0256d8..02e4a6c6 100644 --- a/libi2pd/api.h +++ b/libi2pd/api.h @@ -15,31 +15,41 @@ #include "Destination.h" #include "Streaming.h" -namespace i2p -{ -namespace api -{ - // initialization start and stop - void InitI2P (int argc, char* argv[], const char * appName); - void TerminateI2P (); - void StartI2P (std::shared_ptr logStream = nullptr); - // write system log to logStream, if not specified to .log in application's folder - void StopI2P (); - void RunPeerTest (); // should be called after UPnP +namespace i2p { + namespace api { + // initialization start and stop + void InitI2P(int argc, char *argv[], const char *appName); - // destinations - std::shared_ptr CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); - std::shared_ptr CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, - const std::map * params = nullptr); // transient destinations usually not published - void DestroyLocalDestination (std::shared_ptr dest); + void TerminateI2P(); - // streams - void RequestLeaseSet (std::shared_ptr dest, const i2p::data::IdentHash& remote); - std::shared_ptr CreateStream (std::shared_ptr dest, const i2p::data::IdentHash& remote); - void AcceptStream (std::shared_ptr dest, const i2p::stream::StreamingDestination::Acceptor& acceptor); - void DestroyStream (std::shared_ptr stream); -} + void StartI2P(std::shared_ptr logStream = nullptr); + + // write system log to logStream, if not specified to .log in application's folder + void StopI2P(); + + void RunPeerTest(); // should be called after UPnP + + // destinations + std::shared_ptr + CreateLocalDestination(const i2p::data::PrivateKeys &keys, bool isPublic = true, + const std::map *params = nullptr); + + std::shared_ptr CreateLocalDestination(bool isPublic = false, + i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, + const std::map *params = nullptr); // transient destinations usually not published + void DestroyLocalDestination(std::shared_ptr dest); + + // streams + void RequestLeaseSet(std::shared_ptr dest, const i2p::data::IdentHash &remote); + + std::shared_ptr + CreateStream(std::shared_ptr dest, const i2p::data::IdentHash &remote); + + void AcceptStream(std::shared_ptr dest, + const i2p::stream::StreamingDestination::Acceptor &acceptor); + + void DestroyStream(std::shared_ptr stream); + } } #endif diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 10de346f..a7a31ece 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -15,7 +15,9 @@ #include "I2PEndian.h" #if not defined (__FreeBSD__) + #include + #endif #if defined(__OpenBSD__) || defined(__FreeBSD__) @@ -40,585 +42,548 @@ // This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found int inet_pton_xp (int af, const char *src, void *dst) { - struct sockaddr_storage ss; - int size = sizeof (ss); - char src_copy[INET6_ADDRSTRLEN + 1]; + struct sockaddr_storage ss; + int size = sizeof (ss); + char src_copy[INET6_ADDRSTRLEN + 1]; - ZeroMemory (&ss, sizeof (ss)); - strncpy (src_copy, src, INET6_ADDRSTRLEN + 1); - src_copy[INET6_ADDRSTRLEN] = 0; + ZeroMemory (&ss, sizeof (ss)); + strncpy (src_copy, src, INET6_ADDRSTRLEN + 1); + src_copy[INET6_ADDRSTRLEN] = 0; - if (WSAStringToAddress (src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) - { - switch (af) - { - case AF_INET: - *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; - return 1; - case AF_INET6: - *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; - return 1; - } - } - return 0; + if (WSAStringToAddress (src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) + { + switch (af) + { + case AF_INET: + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + return 1; + case AF_INET6: + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + return 1; + } + } + return 0; } const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size) { - struct sockaddr_storage ss; - unsigned long s = size; + struct sockaddr_storage ss; + unsigned long s = size; - ZeroMemory(&ss, sizeof(ss)); - ss.ss_family = af; + ZeroMemory(&ss, sizeof(ss)); + ss.ss_family = af; - switch(af) { - case AF_INET: - ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; - break; - case AF_INET6: - ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; - break; - default: - return NULL; - } - /* cannot direclty use &size because of strict aliasing rules */ - return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL; + switch(af) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; + break; + default: + return NULL; + } + /* cannot direclty use &size because of strict aliasing rules */ + return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL; } #else /* !_WIN32 => UNIX */ + #include + #ifdef ANDROID #include "ifaddrs.h" #else + #include + #endif #endif -#define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () } -#define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () } +#define address_pair_v4(a, b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () } +#define address_pair_v6(a, b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () } -namespace i2p -{ -namespace util -{ +namespace i2p { + namespace util { - void RunnableService::StartIOService () - { - if (!m_IsRunning) - { - m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this))); - } - } + void RunnableService::StartIOService() { + if (!m_IsRunning) { + m_IsRunning = true; + m_Thread.reset(new std::thread(std::bind(&RunnableService::Run, this))); + } + } - void RunnableService::StopIOService () - { - if (m_IsRunning) - { - m_IsRunning = false; - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - } - } + void RunnableService::StopIOService() { + if (m_IsRunning) { + m_IsRunning = false; + m_Service.stop(); + if (m_Thread) { + m_Thread->join(); + m_Thread = nullptr; + } + } + } - void RunnableService::Run () - { - SetThreadName(m_Name.c_str()); + void RunnableService::Run() { + SetThreadName(m_Name.c_str()); - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, m_Name, ": Runtime exception: ", ex.what ()); - } - } - } + while (m_IsRunning) { + try { + m_Service.run(); + } + catch (std::exception &ex) { + LogPrint(eLogError, m_Name, ": Runtime exception: ", ex.what()); + } + } + } - void SetThreadName (const char *name) { + void SetThreadName(const char *name) { #if defined(__APPLE__) && !defined(__powerpc__) - pthread_setname_np((char*)name); + pthread_setname_np((char*)name); #elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(pthread_self(), name); + pthread_set_name_np(pthread_self(), name); #elif defined(__NetBSD__) - pthread_setname_np(pthread_self(), "%s", (void *)name); + pthread_setname_np(pthread_self(), "%s", (void *)name); #elif !defined(__gnu_hurd__) - pthread_setname_np(pthread_self(), name); + pthread_setname_np(pthread_self(), name); #endif - } + } -namespace net -{ + namespace net { #ifdef _WIN32 - int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback) - { - typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); - IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); - if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found + int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback) + { + typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); + IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); + if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; + ULONG outBufLen = 0; + PIP_ADAPTER_ADDRESSES pAddresses = nullptr; + PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } + if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); + } - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); + DWORD dwRetVal = GetAdaptersAddresses( + AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen + ); - if(dwRetVal != NO_ERROR) - { - LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return fallback; - } + if(dwRetVal != NO_ERROR) + { + LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); + FREE(pAddresses); + return fallback; + } - pCurrAddresses = pAddresses; - while(pCurrAddresses) - { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; + pCurrAddresses = pAddresses; + while(pCurrAddresses) + { + PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - pUnicast = pCurrAddresses->FirstUnicastAddress; - if(pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported"); + pUnicast = pCurrAddresses->FirstUnicastAddress; + if(pUnicast == nullptr) + LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported"); - for(int i = 0; pUnicast != nullptr; ++i) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr; - if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) - { - char addr[INET_ADDRSTRLEN]; - inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN); + for(int i = 0; pUnicast != nullptr; ++i) + { + LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; + sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr; + if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) + { + char addr[INET_ADDRSTRLEN]; + inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN); - auto result = pCurrAddresses->Mtu; - FREE(pAddresses); - pAddresses = nullptr; - LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr); - return result; - } - pUnicast = pUnicast->Next; - } - pCurrAddresses = pCurrAddresses->Next; - } + auto result = pCurrAddresses->Mtu; + FREE(pAddresses); + pAddresses = nullptr; + LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr); + return result; + } + pUnicast = pUnicast->Next; + } + pCurrAddresses = pCurrAddresses->Next; + } - LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found"); - FREE(pAddresses); - return fallback; - } + LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found"); + FREE(pAddresses); + return fallback; + } - int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback) - { - typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); - IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); - if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found + int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback) + { + typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); + IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); + if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; + ULONG outBufLen = 0; + PIP_ADAPTER_ADDRESSES pAddresses = nullptr; + PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); + } - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); + DWORD dwRetVal = GetAdaptersAddresses( + AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen + ); - if (dwRetVal != NO_ERROR) - { - LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return fallback; - } + if (dwRetVal != NO_ERROR) + { + LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); + FREE(pAddresses); + return fallback; + } - bool found_address = false; - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - pUnicast = pCurrAddresses->FirstUnicastAddress; - if (pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported"); + bool found_address = false; + pCurrAddresses = pAddresses; + while (pCurrAddresses) + { + PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast == nullptr) + LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported"); - for (int i = 0; pUnicast != nullptr; ++i) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; + for (int i = 0; pUnicast != nullptr; ++i) + { + LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; + sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; - for (int j = 0; j != 8; ++j) - { - if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j]) - break; - else - found_address = true; - } + for (int j = 0; j != 8; ++j) + { + if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j]) + break; + else + found_address = true; + } - if (found_address) - { - char addr[INET6_ADDRSTRLEN]; - inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN); + if (found_address) + { + char addr[INET6_ADDRSTRLEN]; + inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN); - auto result = pCurrAddresses->Mtu; - FREE(pAddresses); - pAddresses = nullptr; - LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr); - return result; - } - pUnicast = pUnicast->Next; - } + auto result = pCurrAddresses->Mtu; + FREE(pAddresses); + pAddresses = nullptr; + LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr); + return result; + } + pUnicast = pUnicast->Next; + } - pCurrAddresses = pCurrAddresses->Next; - } + pCurrAddresses = pCurrAddresses->Next; + } - LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found"); - FREE(pAddresses); - return fallback; - } + LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found"); + FREE(pAddresses); + return fallback; + } - int GetMTUWindows (const boost::asio::ip::address& localAddress, int fallback) - { + int GetMTUWindows (const boost::asio::ip::address& localAddress, int fallback) + { #ifdef UNICODE - string localAddress_temporary = localAddress.to_string(); - wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end()); + string localAddress_temporary = localAddress.to_string(); + wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end()); #else - std::string localAddressUniversal = localAddress.to_string(); + std::string localAddressUniversal = localAddress.to_string(); #endif - typedef int (* IPN)(int af, const char *src, void *dst); - IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); - if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found + typedef int (* IPN)(int af, const char *src, void *dst); + IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); + if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found - if(localAddress.is_v4()) - { - sockaddr_in inputAddress; - inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); - return GetMTUWindowsIpv4(inputAddress, fallback); - } - else if(localAddress.is_v6()) - { - sockaddr_in6 inputAddress; - inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); - return GetMTUWindowsIpv6(inputAddress, fallback); - } - else - { - LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported"); - return fallback; - } - } + if(localAddress.is_v4()) + { + sockaddr_in inputAddress; + inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); + return GetMTUWindowsIpv4(inputAddress, fallback); + } + else if(localAddress.is_v6()) + { + sockaddr_in6 inputAddress; + inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); + return GetMTUWindowsIpv6(inputAddress, fallback); + } + else + { + LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported"); + return fallback; + } + } #else // assume unix - int GetMTUUnix (const boost::asio::ip::address& localAddress, int fallback) - { - ifaddrs* ifaddr, *ifa = nullptr; - if(getifaddrs(&ifaddr) == -1) - { - LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno)); - return fallback; - } - int family = 0; - // look for interface matching local address - for(ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) - { - if(!ifa->ifa_addr) - continue; + int GetMTUUnix(const boost::asio::ip::address &localAddress, int fallback) { + ifaddrs *ifaddr, *ifa = nullptr; + if (getifaddrs(&ifaddr) == -1) { + LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno)); + return fallback; + } - family = ifa->ifa_addr->sa_family; - if(family == AF_INET && localAddress.is_v4()) - { - sockaddr_in* sa = (sockaddr_in*) ifa->ifa_addr; - if(!memcmp(&sa->sin_addr, localAddress.to_v4().to_bytes().data(), 4)) - break; // address matches - } - else if(family == AF_INET6 && localAddress.is_v6()) - { - sockaddr_in6* sa = (sockaddr_in6*) ifa->ifa_addr; - if(!memcmp(&sa->sin6_addr, localAddress.to_v6().to_bytes().data(), 16)) - break; // address matches - } - } - int mtu = fallback; - if(ifa && family) - { // interface found? - int fd = socket(family, SOCK_DGRAM, 0); - if(fd > 0) - { - ifreq ifr; - strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ-1); // set interface for query - if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0) - mtu = ifr.ifr_mtu; // MTU - else - LogPrint (eLogError, "NetIface: Failed to run ioctl: ", strerror(errno)); - close(fd); - } - else - LogPrint(eLogError, "NetIface: Failed to create datagram socket"); - } - else - LogPrint(eLogWarning, "NetIface: Interface for local address", localAddress.to_string(), " not found"); - freeifaddrs(ifaddr); + int family = 0; + // look for interface matching local address + for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + family = ifa->ifa_addr->sa_family; + if (family == AF_INET && localAddress.is_v4()) { + sockaddr_in *sa = (sockaddr_in *) ifa->ifa_addr; + if (!memcmp(&sa->sin_addr, localAddress.to_v4().to_bytes().data(), 4)) + break; // address matches + } else if (family == AF_INET6 && localAddress.is_v6()) { + sockaddr_in6 *sa = (sockaddr_in6 *) ifa->ifa_addr; + if (!memcmp(&sa->sin6_addr, localAddress.to_v6().to_bytes().data(), 16)) + break; // address matches + } + } + int mtu = fallback; + if (ifa && family) { // interface found? + int fd = socket(family, SOCK_DGRAM, 0); + if (fd > 0) { + ifreq ifr; + strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ - 1); // set interface for query + if (ioctl(fd, SIOCGIFMTU, &ifr) >= 0) + mtu = ifr.ifr_mtu; // MTU + else + LogPrint(eLogError, "NetIface: Failed to run ioctl: ", strerror(errno)); + close(fd); + } else + LogPrint(eLogError, "NetIface: Failed to create datagram socket"); + } else + LogPrint(eLogWarning, "NetIface: Interface for local address", localAddress.to_string(), + " not found"); + freeifaddrs(ifaddr); + + return mtu; + } - return mtu; - } #endif // _WIN32 - int GetMTU (const boost::asio::ip::address& localAddress) - { - int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU + int GetMTU(const boost::asio::ip::address &localAddress) { + int fallback = localAddress.is_v6() ? 1280 : 620; // fallback MTU #ifdef _WIN32 - return GetMTUWindows(localAddress, fallback); + return GetMTUWindows(localAddress, fallback); #else - return GetMTUUnix(localAddress, fallback); + return GetMTUUnix(localAddress, fallback); #endif - return fallback; - } + return fallback; + } - const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6) - { + const boost::asio::ip::address GetInterfaceAddress(const std::string &ifname, bool ipv6) { #ifdef _WIN32 - LogPrint(eLogError, "NetIface: Cannot get address by interface name, not implemented on WIN32"); - if(ipv6) - return boost::asio::ip::address::from_string("::1"); - else - return boost::asio::ip::address::from_string("127.0.0.1"); + LogPrint(eLogError, "NetIface: Cannot get address by interface name, not implemented on WIN32"); + if(ipv6) + return boost::asio::ip::address::from_string("::1"); + else + return boost::asio::ip::address::from_string("127.0.0.1"); #else - int af = (ipv6 ? AF_INET6 : AF_INET); - ifaddrs *addrs; - try - { - if (!getifaddrs(&addrs)) - { - for (auto cur = addrs; cur; cur = cur->ifa_next) - { - std::string cur_ifname(cur->ifa_name); - if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) - { - // match - char addr[INET6_ADDRSTRLEN]; - memset (addr, 0, INET6_ADDRSTRLEN); - if(af == AF_INET) - inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); - else - inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); - freeifaddrs(addrs); - std::string cur_ifaddr(addr); - return boost::asio::ip::address::from_string(cur_ifaddr); - } - } - } - } - catch (std::exception& ex) - { - LogPrint(eLogError, "NetIface: Exception while searching address using ifaddr: ", ex.what()); - } + int af = (ipv6 ? AF_INET6 : AF_INET); + ifaddrs *addrs; + try { + if (!getifaddrs(&addrs)) { + for (auto cur = addrs; cur; cur = cur->ifa_next) { + std::string cur_ifname(cur->ifa_name); + if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) { + // match + char addr[INET6_ADDRSTRLEN]; + memset(addr, 0, INET6_ADDRSTRLEN); + if (af == AF_INET) + inet_ntop(af, &((sockaddr_in *) cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); + else + inet_ntop(af, &((sockaddr_in6 *) cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); + freeifaddrs(addrs); + std::string cur_ifaddr(addr); + return boost::asio::ip::address::from_string(cur_ifaddr); + } + } + } + } + catch (std::exception &ex) { + LogPrint(eLogError, "NetIface: Exception while searching address using ifaddr: ", ex.what()); + } - if(addrs) freeifaddrs(addrs); - std::string fallback; - if(ipv6) - { - fallback = "::1"; - LogPrint(eLogWarning, "NetIface: Cannot find IPv6 address for interface ", ifname); - } else { - fallback = "127.0.0.1"; - LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname); - } - return boost::asio::ip::address::from_string(fallback); + if (addrs) freeifaddrs(addrs); + std::string fallback; + if (ipv6) { + fallback = "::1"; + LogPrint(eLogWarning, "NetIface: Cannot find IPv6 address for interface ", ifname); + } else { + fallback = "127.0.0.1"; + LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname); + } + return boost::asio::ip::address::from_string(fallback); #endif - } + } - int GetMaxMTU (const boost::asio::ip::address_v6& localAddress) - { - uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ()); - switch (prefix) - { - case 0x20010470: - case 0x260070ff: - // Hurricane Electric - return 1480; - break; - case 0x2a06a003: - case 0x2a06a004: - case 0x2a06a005: - // route48 - return 1420; - break; - default: ; - } - return 1500; - } + int GetMaxMTU(const boost::asio::ip::address_v6 &localAddress) { + uint32_t prefix = bufbe32toh(localAddress.to_bytes().data()); + switch (prefix) { + case 0x20010470: + case 0x260070ff: + // Hurricane Electric + return 1480; + break; + case 0x2a06a003: + case 0x2a06a004: + case 0x2a06a005: + // route48 + return 1420; + break; + default:; + } + return 1500; + } - static bool IsYggdrasilAddress (const uint8_t addr[16]) - { - return addr[0] == 0x02 || addr[0] == 0x03; - } + static bool IsYggdrasilAddress(const uint8_t addr[16]) { + return addr[0] == 0x02 || addr[0] == 0x03; + } - bool IsYggdrasilAddress (const boost::asio::ip::address& addr) - { - if (!addr.is_v6 ()) return false; - return IsYggdrasilAddress (addr.to_v6 ().to_bytes ().data ()); - } + bool IsYggdrasilAddress(const boost::asio::ip::address &addr) { + if (!addr.is_v6()) return false; + return IsYggdrasilAddress(addr.to_v6().to_bytes().data()); + } - boost::asio::ip::address_v6 GetYggdrasilAddress () - { + boost::asio::ip::address_v6 GetYggdrasilAddress() { #if defined(_WIN32) - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; + ULONG outBufLen = 0; + PIP_ADAPTER_ADDRESSES pAddresses = nullptr; + PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } + if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); + } - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); + DWORD dwRetVal = GetAdaptersAddresses( + AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen + ); - if(dwRetVal != NO_ERROR) - { - LogPrint(eLogError, "NetIface: GetYggdrasilAddress(): enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return boost::asio::ip::address_v6 (); - } + if(dwRetVal != NO_ERROR) + { + LogPrint(eLogError, "NetIface: GetYggdrasilAddress(): enclosed GetAdaptersAddresses() call has failed"); + FREE(pAddresses); + return boost::asio::ip::address_v6 (); + } - pCurrAddresses = pAddresses; - while(pCurrAddresses) - { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - pUnicast = pCurrAddresses->FirstUnicastAddress; + pCurrAddresses = pAddresses; + while(pCurrAddresses) + { + PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; + pUnicast = pCurrAddresses->FirstUnicastAddress; - for(int i = 0; pUnicast != nullptr; ++i) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; - if (IsYggdrasilAddress(localInterfaceAddress->sin6_addr.u.Byte)) { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), &localInterfaceAddress->sin6_addr.u.Byte, 16); - FREE(pAddresses); - return boost::asio::ip::address_v6 (bytes); - } - pUnicast = pUnicast->Next; - } - pCurrAddresses = pCurrAddresses->Next; - } - LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); - FREE(pAddresses); - return boost::asio::ip::address_v6 (); + for(int i = 0; pUnicast != nullptr; ++i) + { + LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; + sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; + if (IsYggdrasilAddress(localInterfaceAddress->sin6_addr.u.Byte)) { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), &localInterfaceAddress->sin6_addr.u.Byte, 16); + FREE(pAddresses); + return boost::asio::ip::address_v6 (bytes); + } + pUnicast = pUnicast->Next; + } + pCurrAddresses = pCurrAddresses->Next; + } + LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); + FREE(pAddresses); + return boost::asio::ip::address_v6 (); #else - ifaddrs *addrs; - try - { - if (!getifaddrs(&addrs)) - { - for (auto cur = addrs; cur; cur = cur->ifa_next) - { - if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) - { - sockaddr_in6* sa = (sockaddr_in6*)cur->ifa_addr; - if (IsYggdrasilAddress(sa->sin6_addr.s6_addr)) - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), &sa->sin6_addr, 16); - freeifaddrs(addrs); - return boost::asio::ip::address_v6 (bytes); - } - } - } - } - } - catch (std::exception& ex) - { - LogPrint(eLogError, "NetIface: Exception while searching Yggdrasill address using ifaddr: ", ex.what()); - } - LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); - if(addrs) freeifaddrs(addrs); - return boost::asio::ip::address_v6 (); + ifaddrs *addrs; + try { + if (!getifaddrs(&addrs)) { + for (auto cur = addrs; cur; cur = cur->ifa_next) { + if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) { + sockaddr_in6 *sa = (sockaddr_in6 *) cur->ifa_addr; + if (IsYggdrasilAddress(sa->sin6_addr.s6_addr)) { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy(bytes.data(), &sa->sin6_addr, 16); + freeifaddrs(addrs); + return boost::asio::ip::address_v6(bytes); + } + } + } + } + } + catch (std::exception &ex) { + LogPrint(eLogError, "NetIface: Exception while searching Yggdrasill address using ifaddr: ", + ex.what()); + } + LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); + if (addrs) freeifaddrs(addrs); + return boost::asio::ip::address_v6(); #endif - } + } - bool IsLocalAddress (const boost::asio::ip::address& addr) - { - auto mtu = // TODO: implement better + bool IsLocalAddress(const boost::asio::ip::address &addr) { + auto mtu = // TODO: implement better #ifdef _WIN32 - GetMTUWindows(addr, 0); + GetMTUWindows(addr, 0); #else - GetMTUUnix(addr, 0); + GetMTUUnix(addr, 0); #endif - return mtu > 0; - } + return mtu > 0; + } - bool IsInReservedRange (const boost::asio::ip::address& host) - { - // https://en.wikipedia.org/wiki/Reserved_IP_addresses - if (host.is_unspecified ()) return false; - if(host.is_v4()) - { - static const std::vector< std::pair > reservedIPv4Ranges { - address_pair_v4("0.0.0.0", "0.255.255.255"), - address_pair_v4("10.0.0.0", "10.255.255.255"), - address_pair_v4("100.64.0.0", "100.127.255.255"), - address_pair_v4("127.0.0.0", "127.255.255.255"), - address_pair_v4("169.254.0.0", "169.254.255.255"), - address_pair_v4("172.16.0.0", "172.31.255.255"), - address_pair_v4("192.0.0.0", "192.0.0.255"), - address_pair_v4("192.0.2.0", "192.0.2.255"), - address_pair_v4("192.88.99.0", "192.88.99.255"), - address_pair_v4("192.168.0.0", "192.168.255.255"), - address_pair_v4("198.18.0.0", "192.19.255.255"), - address_pair_v4("198.51.100.0", "198.51.100.255"), - address_pair_v4("203.0.113.0", "203.0.113.255"), - address_pair_v4("224.0.0.0", "255.255.255.255") - }; + bool IsInReservedRange(const boost::asio::ip::address &host) { + // https://en.wikipedia.org/wiki/Reserved_IP_addresses + if (host.is_unspecified()) return false; + if (host.is_v4()) { + static const std::vector > reservedIPv4Ranges{ + address_pair_v4("0.0.0.0", "0.255.255.255"), + address_pair_v4("10.0.0.0", "10.255.255.255"), + address_pair_v4("100.64.0.0", "100.127.255.255"), + address_pair_v4("127.0.0.0", "127.255.255.255"), + address_pair_v4("169.254.0.0", "169.254.255.255"), + address_pair_v4("172.16.0.0", "172.31.255.255"), + address_pair_v4("192.0.0.0", "192.0.0.255"), + address_pair_v4("192.0.2.0", "192.0.2.255"), + address_pair_v4("192.88.99.0", "192.88.99.255"), + address_pair_v4("192.168.0.0", "192.168.255.255"), + address_pair_v4("198.18.0.0", "192.19.255.255"), + address_pair_v4("198.51.100.0", "198.51.100.255"), + address_pair_v4("203.0.113.0", "203.0.113.255"), + address_pair_v4("224.0.0.0", "255.255.255.255") + }; - uint32_t ipv4_address = host.to_v4 ().to_ulong (); - for(const auto& it : reservedIPv4Ranges) { - if (ipv4_address >= it.first && ipv4_address <= it.second) - return true; - } - } - if(host.is_v6()) - { - static const std::vector< std::pair > reservedIPv6Ranges { - address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("ff00::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("::", "::"), - address_pair_v6("::1", "::1") - }; + uint32_t ipv4_address = host.to_v4().to_ulong(); + for (const auto &it: reservedIPv4Ranges) { + if (ipv4_address >= it.first && ipv4_address <= it.second) + return true; + } + } + if (host.is_v6()) { + static const std::vector > reservedIPv6Ranges{ + address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), + address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), + address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), + address_pair_v6("ff00::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), + address_pair_v6("::", "::"), + address_pair_v6("::1", "::1") + }; - boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes (); - for(const auto& it : reservedIPv6Ranges) { - if (ipv6_address >= it.first && ipv6_address <= it.second) - return true; - } - if (IsYggdrasilAddress (ipv6_address.data ())) // yggdrasil? - return true; - } - return false; - } -} // net -} // util + boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6().to_bytes(); + for (const auto &it: reservedIPv6Ranges) { + if (ipv6_address >= it.first && ipv6_address <= it.second) + return true; + } + if (IsYggdrasilAddress(ipv6_address.data())) // yggdrasil? + return true; + } + return false; + } + } // net + } // util } // i2p diff --git a/libi2pd/util.h b/libi2pd/util.h index 9420060b..fb7af950 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -22,210 +22,200 @@ #include namespace std { - template - std::string to_string(T value) - { - return boost::lexical_cast(value); - } + template + std::string to_string(T value) + { + return boost::lexical_cast(value); + } - inline int stoi(const std::string& str) - { - return boost::lexical_cast(str); - } + inline int stoi(const std::string& str) + { + return boost::lexical_cast(str); + } } #endif #endif -namespace i2p -{ -namespace util -{ +namespace i2p { + namespace util { - template - class MemoryPool - { - //BOOST_STATIC_ASSERT_MSG(sizeof(T) >= sizeof(void*), "size cannot be less that general pointer size"); + template + class MemoryPool { + //BOOST_STATIC_ASSERT_MSG(sizeof(T) >= sizeof(void*), "size cannot be less that general pointer size"); - public: + public: - MemoryPool (): m_Head (nullptr) {} - ~MemoryPool () - { - CleanUp (); - } + MemoryPool() : m_Head(nullptr) {} - void CleanUp () - { - CleanUp (m_Head); - m_Head = nullptr; - } + ~MemoryPool() { + CleanUp(); + } - template - T * Acquire (TArgs&&... args) - { - if (!m_Head) return new T(std::forward(args)...); - else - { - auto tmp = m_Head; - m_Head = static_cast(*(void * *)m_Head); // next - return new (tmp)T(std::forward(args)...); - } - } + void CleanUp() { + CleanUp(m_Head); + m_Head = nullptr; + } - void Release (T * t) - { - if (!t) return; - t->~T (); - *(void * *)t = m_Head; // next - m_Head = t; - } + template + T *Acquire(TArgs &&... args) { + if (!m_Head) return new T(std::forward(args)...); + else { + auto tmp = m_Head; + m_Head = static_cast(*(void **) m_Head); // next + return new(tmp)T(std::forward(args)...); + } + } - template - std::unique_ptr > AcquireUnique (TArgs&&... args) - { - return std::unique_ptr >(Acquire (std::forward(args)...), - std::bind (&MemoryPool::Release, this, std::placeholders::_1)); - } + void Release(T *t) { + if (!t) return; + t->~T(); + *(void **) t = m_Head; // next + m_Head = t; + } - template - std::shared_ptr AcquireShared (TArgs&&... args) - { - return std::shared_ptr(Acquire (std::forward(args)...), - std::bind (&MemoryPool::Release, this, std::placeholders::_1)); - } + template + std::unique_ptr > AcquireUnique(TArgs &&... args) { + return std::unique_ptr < T, std::function < void(T * ) > > (Acquire(std::forward(args)...), + std::bind(&MemoryPool::Release, this, std::placeholders::_1)); + } - protected: + template + std::shared_ptr AcquireShared(TArgs &&... args) { + return std::shared_ptr(Acquire(std::forward(args)...), + std::bind(&MemoryPool::Release, this, std::placeholders::_1)); + } - void CleanUp (T * head) - { - while (head) - { - auto tmp = head; - head = static_cast(*(void * *)head); // next - ::operator delete ((void *)tmp); - } - } + protected: - protected: + void CleanUp(T *head) { + while (head) { + auto tmp = head; + head = static_cast(*(void **) head); // next + ::operator delete((void *) tmp); + } + } - T * m_Head; - }; + protected: - template - class MemoryPoolMt: public MemoryPool - { - public: + T *m_Head; + }; - MemoryPoolMt () {} - template - T * AcquireMt (TArgs&&... args) - { - if (!this->m_Head) return new T(std::forward(args)...); - std::lock_guard l(m_Mutex); - return this->Acquire (std::forward(args)...); - } + template + class MemoryPoolMt : public MemoryPool { + public: - void ReleaseMt (T * t) - { - std::lock_guard l(m_Mutex); - this->Release (t); - } + MemoryPoolMt() {} - templateclass C, typename... R> - void ReleaseMt(const C& c) - { - std::lock_guard l(m_Mutex); - for (auto& it: c) - this->Release (it); - } + template + T *AcquireMt(TArgs &&... args) { + if (!this->m_Head) return new T(std::forward(args)...); + std::lock_guard l(m_Mutex); + return this->Acquire(std::forward(args)...); + } - template - std::shared_ptr AcquireSharedMt (TArgs&&... args) - { - return std::shared_ptr(AcquireMt (std::forward(args)...), - std::bind::*)(T *)> (&MemoryPoolMt::ReleaseMt, this, std::placeholders::_1)); - } + void ReleaseMt(T *t) { + std::lock_guard l(m_Mutex); + this->Release(t); + } - void CleanUpMt () - { - T * head; - { - std::lock_guard l(m_Mutex); - head = this->m_Head; - this->m_Head = nullptr; - } - if (head) this->CleanUp (head); - } + template class C, typename... R> + void ReleaseMt(const C &c) { + std::lock_guard l(m_Mutex); + for (auto &it: c) + this->Release(it); + } - private: + template + std::shared_ptr AcquireSharedMt(TArgs &&... args) { + return std::shared_ptr(AcquireMt(std::forward(args)...), + std::bind < void(MemoryPoolMt::*)(T * ) > + (&MemoryPoolMt::ReleaseMt, this, std::placeholders::_1)); + } - std::mutex m_Mutex; - }; + void CleanUpMt() { + T *head; + { + std::lock_guard l(m_Mutex); + head = this->m_Head; + this->m_Head = nullptr; + } + if (head) this->CleanUp(head); + } - class RunnableService - { - protected: + private: - RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {} - virtual ~RunnableService () {} + std::mutex m_Mutex; + }; - boost::asio::io_service& GetIOService () { return m_Service; } - bool IsRunning () const { return m_IsRunning; }; + class RunnableService { + protected: - void StartIOService (); - void StopIOService (); + RunnableService(const std::string &name) : m_Name(name), m_IsRunning(false) {} - private: + virtual ~RunnableService() {} - void Run (); + boost::asio::io_service &GetIOService() { return m_Service; } - private: + bool IsRunning() const { return m_IsRunning; }; - std::string m_Name; - volatile bool m_IsRunning; - std::unique_ptr m_Thread; - boost::asio::io_service m_Service; - }; + void StartIOService(); - class RunnableServiceWithWork: public RunnableService - { - protected: + void StopIOService(); - RunnableServiceWithWork (const std::string& name): - RunnableService (name), m_Work (GetIOService ()) {} + private: - private: + void Run(); - boost::asio::io_service::work m_Work; - }; + private: - void SetThreadName (const char *name); + std::string m_Name; + volatile bool m_IsRunning; + std::unique_ptr m_Thread; + boost::asio::io_service m_Service; + }; - template - class SaveStateHelper - { - public: + class RunnableServiceWithWork : public RunnableService { + protected: - SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {}; - ~SaveStateHelper () { m_Original = m_Copy; }; + RunnableServiceWithWork(const std::string &name) : + RunnableService(name), m_Work(GetIOService()) {} - private: + private: - T& m_Original; - T m_Copy; - }; + boost::asio::io_service::work m_Work; + }; - namespace net - { - int GetMTU (const boost::asio::ip::address& localAddress); - int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address - const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false); - boost::asio::ip::address_v6 GetYggdrasilAddress (); - bool IsLocalAddress (const boost::asio::ip::address& addr); - bool IsInReservedRange (const boost::asio::ip::address& host); - bool IsYggdrasilAddress (const boost::asio::ip::address& addr); - } -} + void SetThreadName(const char *name); + + template + class SaveStateHelper { + public: + + SaveStateHelper(T &orig) : m_Original(orig), m_Copy(orig) {}; + + ~SaveStateHelper() { m_Original = m_Copy; }; + + private: + + T &m_Original; + T m_Copy; + }; + + namespace net { + int GetMTU(const boost::asio::ip::address &localAddress); + + int GetMaxMTU(const boost::asio::ip::address_v6 &localAddress); // check tunnel broker for ipv6 address + const boost::asio::ip::address GetInterfaceAddress(const std::string &ifname, bool ipv6 = false); + + boost::asio::ip::address_v6 GetYggdrasilAddress(); + + bool IsLocalAddress(const boost::asio::ip::address &addr); + + bool IsInReservedRange(const boost::asio::ip::address &host); + + bool IsYggdrasilAddress(const boost::asio::ip::address &addr); + } + } } #endif diff --git a/libi2pd/version.h b/libi2pd/version.h index 5b6925cc..dbd75683 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -12,17 +12,17 @@ #define CODENAME "Purple" #define STRINGIZE(x) #x -#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) -#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) +#define MAKE_VERSION(a, b, c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) +#define MAKE_VERSION_NUMBER(a, b, c) ((a*100+b)*100+c) #define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MINOR 43 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #ifdef GITVER - #define I2PD_VERSION GITVER +#define I2PD_VERSION GITVER #else - #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) +#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #endif #define VERSION I2PD_VERSION diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index e67e3f06..2350017f 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -27,998 +27,898 @@ #include "AddressBook.h" #include "Config.h" -namespace i2p -{ -namespace client -{ - // TODO: this is actually proxy class - class AddressBookFilesystemStorage: public AddressBookStorage - { - public: +namespace i2p { + namespace client { + // TODO: this is actually proxy class + class AddressBookFilesystemStorage : public AddressBookStorage { + public: - AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") - { - i2p::config::GetOption("persist.addressbook", m_IsPersist); - if (m_IsPersist) - i2p::config::GetOption("addressbook.hostsfile", m_HostsFile); - } - std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) const; - void AddAddress (std::shared_ptr address); - void RemoveAddress (const i2p::data::IdentHash& ident); + AddressBookFilesystemStorage() : storage("addressbook", "b", "", "b32") { + i2p::config::GetOption("persist.addressbook", m_IsPersist); + if (m_IsPersist) + i2p::config::GetOption("addressbook.hostsfile", m_HostsFile); + } - bool Init (); - int Load (std::map > & addresses); - int LoadLocal (std::map >& addresses); - int Save (const std::map >& addresses); + std::shared_ptr GetAddress(const i2p::data::IdentHash &ident) const; - void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified); - bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified); - void ResetEtags (); + void AddAddress(std::shared_ptr address); - private: + void RemoveAddress(const i2p::data::IdentHash &ident); - int LoadFromFile (const std::string& filename, std::map >& addresses); // returns -1 if can't open file, otherwise number of records + bool Init(); - private: + int Load(std::map > &addresses); - i2p::fs::HashedStorage storage; - std::string etagsPath, indexPath, localPath; - bool m_IsPersist; - std::string m_HostsFile; // file to dump hosts.txt, empty if not used - }; + int LoadLocal(std::map > &addresses); - bool AddressBookFilesystemStorage::Init() - { - storage.SetPlace(i2p::fs::GetDataDir()); - // init storage - if (storage.Init(i2p::data::GetBase32SubstitutionTable(), 32)) - { - // init ETags - etagsPath = i2p::fs::StorageRootPath (storage, "etags"); - if (!i2p::fs::Exists (etagsPath)) - i2p::fs::CreateDirectory (etagsPath); - // init address files - indexPath = i2p::fs::StorageRootPath (storage, "addresses.csv"); - localPath = i2p::fs::StorageRootPath (storage, "local.csv"); - return true; - } - return false; - } + int Save(const std::map > &addresses); - std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const - { - if (!m_IsPersist) - { - LogPrint(eLogDebug, "Addressbook: Persistence is disabled"); - return nullptr; - } - std::string filename = storage.Path(ident.ToBase32()); - std::ifstream f(filename, std::ifstream::binary); - if (!f.is_open ()) { - LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename); - return nullptr; - } + void + SaveEtag(const i2p::data::IdentHash &subsciption, const std::string &etag, const std::string &lastModified); - f.seekg (0,std::ios::end); - size_t len = f.tellg (); - if (len < i2p::data::DEFAULT_IDENTITY_SIZE) { - LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len); - return nullptr; - } - f.seekg(0, std::ios::beg); - uint8_t * buf = new uint8_t[len]; - f.read((char *)buf, len); - auto address = std::make_shared(buf, len); - delete[] buf; - return address; - } + bool GetEtag(const i2p::data::IdentHash &subscription, std::string &etag, std::string &lastModified); - void AddressBookFilesystemStorage::AddAddress (std::shared_ptr address) - { - if (!m_IsPersist) return; - std::string path = storage.Path( address->GetIdentHash().ToBase32() ); - std::ofstream f (path, std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) { - LogPrint (eLogError, "Addressbook: Can't open file ", path); - return; - } - size_t len = address->GetFullLen (); - uint8_t * buf = new uint8_t[len]; - address->ToBuffer (buf, len); - f.write ((char *)buf, len); - delete[] buf; - } + void ResetEtags(); - void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) - { - if (!m_IsPersist) return; - storage.Remove( ident.ToBase32() ); - } + private: - int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map >& addresses) - { - int num = 0; - std::ifstream f (filename, std::ifstream::in); // in text mode - if (!f) return -1; + int LoadFromFile(const std::string &filename, + std::map > &addresses); // returns -1 if can't open file, otherwise number of records - addresses.clear (); - while (!f.eof ()) - { - std::string s; - getline(f, s); - if (!s.length()) continue; // skip empty line + private: - std::size_t pos = s.find(','); - if (pos != std::string::npos) - { - std::string name = s.substr(0, pos++); - std::string addr = s.substr(pos); + i2p::fs::HashedStorage storage; + std::string etagsPath, indexPath, localPath; + bool m_IsPersist; + std::string m_HostsFile; // file to dump hosts.txt, empty if not used + }; - addresses[name] = std::make_shared
(addr); - num++; - } - } - return num; - } + bool AddressBookFilesystemStorage::Init() { + storage.SetPlace(i2p::fs::GetDataDir()); + // init storage + if (storage.Init(i2p::data::GetBase32SubstitutionTable(), 32)) { + // init ETags + etagsPath = i2p::fs::StorageRootPath(storage, "etags"); + if (!i2p::fs::Exists(etagsPath)) + i2p::fs::CreateDirectory(etagsPath); + // init address files + indexPath = i2p::fs::StorageRootPath(storage, "addresses.csv"); + localPath = i2p::fs::StorageRootPath(storage, "local.csv"); + return true; + } + return false; + } - int AddressBookFilesystemStorage::Load (std::map >& addresses) - { - int num = LoadFromFile (indexPath, addresses); - if (num < 0) - { - LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath); - return 0; - } - LogPrint(eLogInfo, "Addressbook: Using index file ", indexPath); - LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage"); + std::shared_ptr + AddressBookFilesystemStorage::GetAddress(const i2p::data::IdentHash &ident) const { + if (!m_IsPersist) { + LogPrint(eLogDebug, "Addressbook: Persistence is disabled"); + return nullptr; + } + std::string filename = storage.Path(ident.ToBase32()); + std::ifstream f(filename, std::ifstream::binary); + if (!f.is_open()) { + LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename); + return nullptr; + } - return num; - } + f.seekg(0, std::ios::end); + size_t len = f.tellg(); + if (len < i2p::data::DEFAULT_IDENTITY_SIZE) { + LogPrint(eLogError, "Addressbook: File ", filename, " is too short: ", len); + return nullptr; + } + f.seekg(0, std::ios::beg); + uint8_t *buf = new uint8_t[len]; + f.read((char *) buf, len); + auto address = std::make_shared(buf, len); + delete[] buf; + return address; + } - int AddressBookFilesystemStorage::LoadLocal (std::map >& addresses) - { - int num = LoadFromFile (localPath, addresses); - if (num < 0) return 0; - LogPrint (eLogInfo, "Addressbook: ", num, " local addresses loaded"); - return num; - } + void AddressBookFilesystemStorage::AddAddress(std::shared_ptr address) { + if (!m_IsPersist) return; + std::string path = storage.Path(address->GetIdentHash().ToBase32()); + std::ofstream f(path, std::ofstream::binary | std::ofstream::out); + if (!f.is_open()) { + LogPrint(eLogError, "Addressbook: Can't open file ", path); + return; + } + size_t len = address->GetFullLen(); + uint8_t *buf = new uint8_t[len]; + address->ToBuffer(buf, len); + f.write((char *) buf, len); + delete[] buf; + } - int AddressBookFilesystemStorage::Save (const std::map >& addresses) - { - if (addresses.empty()) - { - LogPrint(eLogWarning, "Addressbook: Not saving empty addressbook"); - return 0; - } + void AddressBookFilesystemStorage::RemoveAddress(const i2p::data::IdentHash &ident) { + if (!m_IsPersist) return; + storage.Remove(ident.ToBase32()); + } - int num = 0; - { - // save index file - std::ofstream f (indexPath, std::ofstream::out); // in text mode - if (f.is_open ()) - { - for (const auto& it: addresses) - { - if (it.second->IsValid ()) - { - f << it.first << ","; - if (it.second->IsIdentHash ()) - f << it.second->identHash.ToBase32 (); - else - f << it.second->blindedPublicKey->ToB33 (); - f << std::endl; - num++; - } - else - LogPrint (eLogWarning, "Addressbook: Invalid address ", it.first); - } - LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); - } - else - LogPrint (eLogWarning, "Addressbook: Can't open ", indexPath); - } - if (!m_HostsFile.empty ()) - { - // dump full hosts.txt - std::ofstream f (m_HostsFile, std::ofstream::out); // in text mode - if (f.is_open ()) - { - for (const auto& it: addresses) - { - std::shared_ptr addr; - if (it.second->IsIdentHash ()) - { - addr = GetAddress (it.second->identHash); - if (addr) - f << it.first << "=" << addr->ToBase64 () << std::endl; - } - } - } - else - LogPrint (eLogWarning, "Addressbook: Can't open ", m_HostsFile); - } + int AddressBookFilesystemStorage::LoadFromFile(const std::string &filename, + std::map > &addresses) { + int num = 0; + std::ifstream f(filename, std::ifstream::in); // in text mode + if (!f) return -1; - return num; - } + addresses.clear(); + while (!f.eof()) { + std::string s; + getline(f, s); + if (!s.length()) continue; // skip empty line - void AddressBookFilesystemStorage::SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) - { - std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt"; - std::ofstream f (fname, std::ofstream::out | std::ofstream::trunc); - if (f) - { - f << etag << std::endl; - f<< lastModified << std::endl; - } - } + std::size_t pos = s.find(','); + if (pos != std::string::npos) { + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); - bool AddressBookFilesystemStorage::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) - { - std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt"; - std::ifstream f (fname, std::ofstream::in); - if (!f || f.eof ()) return false; - std::getline (f, etag); - if (f.eof ()) return false; - std::getline (f, lastModified); - return true; - } + addresses[name] = std::make_shared
(addr); + num++; + } + } + return num; + } - void AddressBookFilesystemStorage::ResetEtags () - { - LogPrint (eLogError, "Addressbook: Resetting eTags"); - for (boost::filesystem::directory_iterator it (etagsPath); it != boost::filesystem::directory_iterator (); ++it) - { - if (!boost::filesystem::is_regular_file (it->status ())) - continue; - boost::filesystem::remove (it->path ()); - } - } + int AddressBookFilesystemStorage::Load(std::map > &addresses) { + int num = LoadFromFile(indexPath, addresses); + if (num < 0) { + LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath); + return 0; + } + LogPrint(eLogInfo, "Addressbook: Using index file ", indexPath); + LogPrint(eLogInfo, "Addressbook: ", num, " addresses loaded from storage"); + + return num; + } + + int AddressBookFilesystemStorage::LoadLocal(std::map > &addresses) { + int num = LoadFromFile(localPath, addresses); + if (num < 0) return 0; + LogPrint(eLogInfo, "Addressbook: ", num, " local addresses loaded"); + return num; + } + + int AddressBookFilesystemStorage::Save(const std::map > &addresses) { + if (addresses.empty()) { + LogPrint(eLogWarning, "Addressbook: Not saving empty addressbook"); + return 0; + } + + int num = 0; + { + // save index file + std::ofstream f(indexPath, std::ofstream::out); // in text mode + if (f.is_open()) { + for (const auto &it: addresses) { + if (it.second->IsValid()) { + f << it.first << ","; + if (it.second->IsIdentHash()) + f << it.second->identHash.ToBase32(); + else + f << it.second->blindedPublicKey->ToB33(); + f << std::endl; + num++; + } else + LogPrint(eLogWarning, "Addressbook: Invalid address ", it.first); + } + LogPrint(eLogInfo, "Addressbook: ", num, " addresses saved"); + } else + LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath); + } + if (!m_HostsFile.empty()) { + // dump full hosts.txt + std::ofstream f(m_HostsFile, std::ofstream::out); // in text mode + if (f.is_open()) { + for (const auto &it: addresses) { + std::shared_ptr addr; + if (it.second->IsIdentHash()) { + addr = GetAddress(it.second->identHash); + if (addr) + f << it.first << "=" << addr->ToBase64() << std::endl; + } + } + } else + LogPrint(eLogWarning, "Addressbook: Can't open ", m_HostsFile); + } + + return num; + } + + void AddressBookFilesystemStorage::SaveEtag(const i2p::data::IdentHash &subscription, const std::string &etag, + const std::string &lastModified) { + std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32() + ".txt"; + std::ofstream f(fname, std::ofstream::out | std::ofstream::trunc); + if (f) { + f << etag << std::endl; + f << lastModified << std::endl; + } + } + + bool AddressBookFilesystemStorage::GetEtag(const i2p::data::IdentHash &subscription, std::string &etag, + std::string &lastModified) { + std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32() + ".txt"; + std::ifstream f(fname, std::ofstream::in); + if (!f || f.eof()) return false; + std::getline(f, etag); + if (f.eof()) return false; + std::getline(f, lastModified); + return true; + } + + void AddressBookFilesystemStorage::ResetEtags() { + LogPrint(eLogError, "Addressbook: Resetting eTags"); + for (boost::filesystem::directory_iterator it(etagsPath); + it != boost::filesystem::directory_iterator(); ++it) { + if (!boost::filesystem::is_regular_file(it->status())) + continue; + boost::filesystem::remove(it->path()); + } + } //--------------------------------------------------------------------- - Address::Address (const std::string& b32): - addressType (eAddressInvalid) - { - if (b32.length () <= B33_ADDRESS_THRESHOLD) - { - if (identHash.FromBase32 (b32) > 0) - addressType = eAddressIndentHash; - } - else - { - blindedPublicKey = std::make_shared(b32); - if (blindedPublicKey->IsValid ()) - addressType = eAddressBlindedPublicKey; - } - } + Address::Address(const std::string &b32) : + addressType(eAddressInvalid) { + if (b32.length() <= B33_ADDRESS_THRESHOLD) { + if (identHash.FromBase32(b32) > 0) + addressType = eAddressIndentHash; + } else { + blindedPublicKey = std::make_shared(b32); + if (blindedPublicKey->IsValid()) + addressType = eAddressBlindedPublicKey; + } + } - Address::Address (const i2p::data::IdentHash& hash) - { - addressType = eAddressIndentHash; - identHash = hash; - } + Address::Address(const i2p::data::IdentHash &hash) { + addressType = eAddressIndentHash; + identHash = hash; + } - AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), - m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr), - m_IsEnabled (true) - { - } + AddressBook::AddressBook() : m_Storage(nullptr), m_IsLoaded(false), m_IsDownloading(false), + m_NumRetries(0), m_DefaultSubscription(nullptr), + m_SubscriptionsUpdateTimer(nullptr), + m_IsEnabled(true) { + } - AddressBook::~AddressBook () - { - Stop (); - } + AddressBook::~AddressBook() { + Stop(); + } - void AddressBook::Start () - { - i2p::config::GetOption("addressbook.enabled", m_IsEnabled); - if (m_IsEnabled) - { - if (!m_Storage) - m_Storage = new AddressBookFilesystemStorage; - m_Storage->Init(); - LoadHosts (); /* try storage, then hosts.txt, then download */ - StartSubscriptions (); - StartLookups (); - } - } + void AddressBook::Start() { + i2p::config::GetOption("addressbook.enabled", m_IsEnabled); + if (m_IsEnabled) { + if (!m_Storage) + m_Storage = new AddressBookFilesystemStorage; + m_Storage->Init(); + LoadHosts(); /* try storage, then hosts.txt, then download */ + StartSubscriptions(); + StartLookups(); + } + } - void AddressBook::StartResolvers () - { - LoadLocal (); - } + void AddressBook::StartResolvers() { + LoadLocal(); + } - void AddressBook::Stop () - { - StopLookups (); - StopSubscriptions (); - if (m_SubscriptionsUpdateTimer) - { - delete m_SubscriptionsUpdateTimer; - m_SubscriptionsUpdateTimer = nullptr; - } - if (m_IsDownloading) - { - LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort"); - for (int i = 0; i < 30; i++) - { - if (!m_IsDownloading) - { - LogPrint (eLogInfo, "Addressbook: Subscriptions download complete"); - break; - } - std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds - } - LogPrint (eLogError, "Addressbook: Subscription download timeout"); - m_IsDownloading = false; - } - if (m_Storage) - { - m_Storage->Save (m_Addresses); - delete m_Storage; - m_Storage = nullptr; - } - m_DefaultSubscription = nullptr; - m_Subscriptions.clear (); - } + void AddressBook::Stop() { + StopLookups(); + StopSubscriptions(); + if (m_SubscriptionsUpdateTimer) { + delete m_SubscriptionsUpdateTimer; + m_SubscriptionsUpdateTimer = nullptr; + } + if (m_IsDownloading) { + LogPrint(eLogInfo, "Addressbook: Subscriptions are downloading, abort"); + for (int i = 0; i < 30; i++) { + if (!m_IsDownloading) { + LogPrint(eLogInfo, "Addressbook: Subscriptions download complete"); + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for 1 seconds + } + LogPrint(eLogError, "Addressbook: Subscription download timeout"); + m_IsDownloading = false; + } + if (m_Storage) { + m_Storage->Save(m_Addresses); + delete m_Storage; + m_Storage = nullptr; + } + m_DefaultSubscription = nullptr; + m_Subscriptions.clear(); + } - std::shared_ptr AddressBook::GetAddress (const std::string& address) - { - auto pos = address.find(".b32.i2p"); - if (pos != std::string::npos) - { - auto addr = std::make_shared(address.substr (0, pos)); - return addr->IsValid () ? addr : nullptr; - } - else - { - pos = address.find (".i2p"); - if (pos != std::string::npos) - { - if (!m_IsEnabled) return nullptr; - auto addr = FindAddress (address); - if (!addr) - LookupAddress (address); // TODO: - return addr; - } - } - // if not .b32 we assume full base64 address - i2p::data::IdentityEx dest; - if (!dest.FromBase64 (address)) - return nullptr; - return std::make_shared(dest.GetIdentHash ()); - } + std::shared_ptr AddressBook::GetAddress(const std::string &address) { + auto pos = address.find(".b32.i2p"); + if (pos != std::string::npos) { + auto addr = std::make_shared(address.substr(0, pos)); + return addr->IsValid() ? addr : nullptr; + } else { + pos = address.find(".i2p"); + if (pos != std::string::npos) { + if (!m_IsEnabled) return nullptr; + auto addr = FindAddress(address); + if (!addr) + LookupAddress(address); // TODO: + return addr; + } + } + // if not .b32 we assume full base64 address + i2p::data::IdentityEx dest; + if (!dest.FromBase64(address)) + return nullptr; + return std::make_shared(dest.GetIdentHash()); + } - std::shared_ptr AddressBook::FindAddress (const std::string& address) - { - auto it = m_Addresses.find (address); - if (it != m_Addresses.end ()) - return it->second; - return nullptr; - } + std::shared_ptr AddressBook::FindAddress(const std::string &address) { + auto it = m_Addresses.find(address); + if (it != m_Addresses.end()) + return it->second; + return nullptr; + } - void AddressBook::InsertAddress (const std::string& address, const std::string& jump) - { - auto pos = jump.find(".b32.i2p"); - if (pos != std::string::npos) - { - m_Addresses[address] = std::make_shared
(jump.substr (0, pos)); - LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", jump); - } - else - { - // assume base64 - auto ident = std::make_shared(); - if (ident->FromBase64 (jump)) - { - m_Storage->AddAddress (ident); - m_Addresses[address] = std::make_shared
(ident->GetIdentHash ()); - LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", ToAddress(ident->GetIdentHash ())); - } - else - LogPrint (eLogError, "Addressbook: Malformed address ", jump); - } - } + void AddressBook::InsertAddress(const std::string &address, const std::string &jump) { + auto pos = jump.find(".b32.i2p"); + if (pos != std::string::npos) { + m_Addresses[address] = std::make_shared
(jump.substr(0, pos)); + LogPrint(eLogInfo, "Addressbook: Added ", address, " -> ", jump); + } else { + // assume base64 + auto ident = std::make_shared(); + if (ident->FromBase64(jump)) { + m_Storage->AddAddress(ident); + m_Addresses[address] = std::make_shared
(ident->GetIdentHash()); + LogPrint(eLogInfo, "Addressbook: Added ", address, " -> ", ToAddress(ident->GetIdentHash())); + } else + LogPrint(eLogError, "Addressbook: Malformed address ", jump); + } + } - void AddressBook::InsertFullAddress (std::shared_ptr address) - { - m_Storage->AddAddress (address); - } + void AddressBook::InsertFullAddress(std::shared_ptr address) { + m_Storage->AddAddress(address); + } - std::shared_ptr AddressBook::GetFullAddress (const std::string& address) - { - auto addr = GetAddress (address); - if (!addr || !addr->IsIdentHash ()) return nullptr; - return m_Storage->GetAddress (addr->identHash); - } + std::shared_ptr AddressBook::GetFullAddress(const std::string &address) { + auto addr = GetAddress(address); + if (!addr || !addr->IsIdentHash()) return nullptr; + return m_Storage->GetAddress(addr->identHash); + } - void AddressBook::LoadHosts () - { - if (m_Storage->Load (m_Addresses) > 0) - { - m_IsLoaded = true; - return; - } + void AddressBook::LoadHosts() { + if (m_Storage->Load(m_Addresses) > 0) { + m_IsLoaded = true; + return; + } - // then try hosts.txt - std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode - if (f.is_open ()) - { - LoadHostsFromStream (f, false); - m_IsLoaded = true; - } + // then try hosts.txt + std::ifstream f(i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode + if (f.is_open()) { + LoadHostsFromStream(f, false); + m_IsLoaded = true; + } - // reset eTags, because we don’t know how old hosts.txt is or can't load addressbook - m_Storage->ResetEtags (); - } + // reset eTags, because we don’t know how old hosts.txt is or can't load addressbook + m_Storage->ResetEtags(); + } - bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update) - { - std::unique_lock l(m_AddressBookMutex); - int numAddresses = 0; - bool incomplete = false; - std::string s; - while (!f.eof ()) - { - getline(f, s); + bool AddressBook::LoadHostsFromStream(std::istream &f, bool is_update) { + std::unique_lock l(m_AddressBookMutex); + int numAddresses = 0; + bool incomplete = false; + std::string s; + while (!f.eof()) { + getline(f, s); - if (!s.length() || s[0] == '#') - continue; // skip empty or comment line + if (!s.length() || s[0] == '#') + continue; // skip empty or comment line - size_t pos = s.find('='); + size_t pos = s.find('='); - if (pos != std::string::npos) - { - std::string name = s.substr(0, pos++); - std::string addr = s.substr(pos); + if (pos != std::string::npos) { + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); - size_t pos = addr.find('#'); - if (pos != std::string::npos) - addr = addr.substr(0, pos); // remove comments + size_t pos = addr.find('#'); + if (pos != std::string::npos) + addr = addr.substr(0, pos); // remove comments - pos = name.find(".b32.i2p"); - if (pos != std::string::npos) - { - LogPrint (eLogError, "Addressbook: Skipped adding of b32 address: ", name); - continue; - } + pos = name.find(".b32.i2p"); + if (pos != std::string::npos) { + LogPrint(eLogError, "Addressbook: Skipped adding of b32 address: ", name); + continue; + } - pos = name.find(".i2p"); - if (pos == std::string::npos) - { - LogPrint (eLogError, "Addressbook: Malformed domain: ", name); - continue; - } + pos = name.find(".i2p"); + if (pos == std::string::npos) { + LogPrint(eLogError, "Addressbook: Malformed domain: ", name); + continue; + } - auto ident = std::make_shared (); - if (!ident->FromBase64(addr)) { - LogPrint (eLogError, "Addressbook: Malformed address ", addr, " for ", name); - incomplete = f.eof (); - continue; - } - numAddresses++; - auto it = m_Addresses.find (name); - if (it != m_Addresses.end ()) // already exists ? - { - if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash () && // address changed? - ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA - { - it->second->identHash = ident->GetIdentHash (); - m_Storage->AddAddress (ident); - m_Storage->RemoveAddress (it->second->identHash); - LogPrint (eLogInfo, "Addressbook: Updated host: ", name); - } - } - else - { - m_Addresses.emplace (name, std::make_shared
(ident->GetIdentHash ())); - m_Storage->AddAddress (ident); - if (is_update) - LogPrint (eLogInfo, "Addressbook: Added new host: ", name); - } - } - else - incomplete = f.eof (); - } - LogPrint (eLogInfo, "Addressbook: ", numAddresses, " addresses processed"); - if (numAddresses > 0) - { - if (!incomplete) m_IsLoaded = true; - m_Storage->Save (m_Addresses); - } - return !incomplete; - } + auto ident = std::make_shared(); + if (!ident->FromBase64(addr)) { + LogPrint(eLogError, "Addressbook: Malformed address ", addr, " for ", name); + incomplete = f.eof(); + continue; + } + numAddresses++; + auto it = m_Addresses.find(name); + if (it != m_Addresses.end()) // already exists ? + { + if (it->second->IsIdentHash() && it->second->identHash != ident->GetIdentHash() && + // address changed? + ident->GetSigningKeyType() != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA + { + it->second->identHash = ident->GetIdentHash(); + m_Storage->AddAddress(ident); + m_Storage->RemoveAddress(it->second->identHash); + LogPrint(eLogInfo, "Addressbook: Updated host: ", name); + } + } else { + m_Addresses.emplace(name, std::make_shared
(ident->GetIdentHash())); + m_Storage->AddAddress(ident); + if (is_update) + LogPrint(eLogInfo, "Addressbook: Added new host: ", name); + } + } else + incomplete = f.eof(); + } + LogPrint(eLogInfo, "Addressbook: ", numAddresses, " addresses processed"); + if (numAddresses > 0) { + if (!incomplete) m_IsLoaded = true; + m_Storage->Save(m_Addresses); + } + return !incomplete; + } - void AddressBook::LoadSubscriptions () - { - if (!m_Subscriptions.size ()) - { - std::ifstream f (i2p::fs::DataDirPath ("subscriptions.txt"), std::ifstream::in); // in text mode - if (f.is_open ()) - { - std::string s; - while (!f.eof ()) - { - getline(f, s); - if (s.empty () || s[0] == '#') continue; // skip empty line or comment - m_Subscriptions.push_back (std::make_shared (*this, s)); - } - LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); - LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead"); - } - else - { - LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config file"); - // using config file items - std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); - std::vector subsList; - boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on); + void AddressBook::LoadSubscriptions() { + if (!m_Subscriptions.size()) { + std::ifstream f(i2p::fs::DataDirPath("subscriptions.txt"), std::ifstream::in); // in text mode + if (f.is_open()) { + std::string s; + while (!f.eof()) { + getline(f, s); + if (s.empty() || s[0] == '#') continue; // skip empty line or comment + m_Subscriptions.push_back(std::make_shared(*this, s)); + } + LogPrint(eLogInfo, "Addressbook: ", m_Subscriptions.size(), " subscriptions urls loaded"); + LogPrint(eLogWarning, + "Addressbook: subscriptions.txt usage is deprecated, use config file instead"); + } else { + LogPrint(eLogInfo, "Addressbook: Loading subscriptions from config file"); + // using config file items + std::string subscriptionURLs; + i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); + std::vector subsList; + boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on); - for (const auto& s: subsList) - { - m_Subscriptions.push_back (std::make_shared (*this, s)); - } - LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); - } - } - else - LogPrint (eLogError, "Addressbook: Subscriptions already loaded"); - } + for (const auto &s: subsList) { + m_Subscriptions.push_back(std::make_shared(*this, s)); + } + LogPrint(eLogInfo, "Addressbook: ", m_Subscriptions.size(), " subscriptions urls loaded"); + } + } else + LogPrint(eLogError, "Addressbook: Subscriptions already loaded"); + } - void AddressBook::LoadLocal () - { - std::map> localAddresses; - m_Storage->LoadLocal (localAddresses); - for (const auto& it: localAddresses) - { - if (!it.second->IsIdentHash ()) continue; // skip blinded for now - auto dot = it.first.find ('.'); - if (dot != std::string::npos) - { - auto domain = it.first.substr (dot + 1); - auto it1 = m_Addresses.find (domain); // find domain in our addressbook - if (it1 != m_Addresses.end () && it1->second->IsIdentHash ()) - { - auto dest = context.FindLocalDestination (it1->second->identHash); - if (dest) - { - // address is ours - std::shared_ptr resolver; - auto it2 = m_Resolvers.find (it1->second->identHash); - if (it2 != m_Resolvers.end ()) - resolver = it2->second; // resolver exists - else - { - // create new resolver - resolver = std::make_shared(dest); - m_Resolvers.insert (std::make_pair(it1->second->identHash, resolver)); - } - resolver->AddAddress (it.first, it.second->identHash); - } - } - } - } - } + void AddressBook::LoadLocal() { + std::map > localAddresses; + m_Storage->LoadLocal(localAddresses); + for (const auto &it: localAddresses) { + if (!it.second->IsIdentHash()) continue; // skip blinded for now + auto dot = it.first.find('.'); + if (dot != std::string::npos) { + auto domain = it.first.substr(dot + 1); + auto it1 = m_Addresses.find(domain); // find domain in our addressbook + if (it1 != m_Addresses.end() && it1->second->IsIdentHash()) { + auto dest = context.FindLocalDestination(it1->second->identHash); + if (dest) { + // address is ours + std::shared_ptr resolver; + auto it2 = m_Resolvers.find(it1->second->identHash); + if (it2 != m_Resolvers.end()) + resolver = it2->second; // resolver exists + else { + // create new resolver + resolver = std::make_shared(dest); + m_Resolvers.insert(std::make_pair(it1->second->identHash, resolver)); + } + resolver->AddAddress(it.first, it.second->identHash); + } + } + } + } + } - bool AddressBook::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) - { - if (m_Storage) - return m_Storage->GetEtag (subscription, etag, lastModified); - else - return false; - } + bool + AddressBook::GetEtag(const i2p::data::IdentHash &subscription, std::string &etag, std::string &lastModified) { + if (m_Storage) + return m_Storage->GetEtag(subscription, etag, lastModified); + else + return false; + } - void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) - { - m_IsDownloading = false; - m_NumRetries++; - int nextUpdateTimeout = m_NumRetries*CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT; - if (m_NumRetries > CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES || nextUpdateTimeout > CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT) - nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; - if (success) - { - m_NumRetries = 0; - if (m_DefaultSubscription) m_DefaultSubscription = nullptr; - if (m_IsLoaded) - nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; - else - m_IsLoaded = true; - if (m_Storage) m_Storage->SaveEtag (subscription, etag, lastModified); - } - if (m_SubscriptionsUpdateTimer) - { - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(nextUpdateTimeout)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - } + void + AddressBook::DownloadComplete(bool success, const i2p::data::IdentHash &subscription, const std::string &etag, + const std::string &lastModified) { + m_IsDownloading = false; + m_NumRetries++; + int nextUpdateTimeout = m_NumRetries * CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT; + if (m_NumRetries > CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES || + nextUpdateTimeout > CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT) + nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; + if (success) { + m_NumRetries = 0; + if (m_DefaultSubscription) m_DefaultSubscription = nullptr; + if (m_IsLoaded) + nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; + else + m_IsLoaded = true; + if (m_Storage) m_Storage->SaveEtag(subscription, etag, lastModified); + } + if (m_SubscriptionsUpdateTimer) { + m_SubscriptionsUpdateTimer->expires_from_now(boost::posix_time::minutes(nextUpdateTimeout)); + m_SubscriptionsUpdateTimer->async_wait(std::bind(&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + } - void AddressBook::StartSubscriptions () - { - LoadSubscriptions (); - if (m_IsLoaded && m_Subscriptions.empty ()) return; + void AddressBook::StartSubscriptions() { + LoadSubscriptions(); + if (m_IsLoaded && m_Subscriptions.empty()) return; - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ()); - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - else - LogPrint (eLogError, "Addressbook: Can't start subscriptions: missing shared local destination"); - } + auto dest = i2p::client::context.GetSharedLocalDestination(); + if (dest) { + m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer(dest->GetService()); + m_SubscriptionsUpdateTimer->expires_from_now( + boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait(std::bind(&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } else + LogPrint(eLogError, "Addressbook: Can't start subscriptions: missing shared local destination"); + } - void AddressBook::StopSubscriptions () - { - if (m_SubscriptionsUpdateTimer) - m_SubscriptionsUpdateTimer->cancel (); - } + void AddressBook::StopSubscriptions() { + if (m_SubscriptionsUpdateTimer) + m_SubscriptionsUpdateTimer->cancel(); + } - void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (!dest) { - LogPrint(eLogWarning, "Addressbook: Missing local destination, skip subscription update"); - return; - } - if (!m_IsDownloading && dest->IsReady ()) - { - if (!m_IsLoaded) - { - // download it from default subscription - LogPrint (eLogInfo, "Addressbook: Trying to download it from default subscription."); - std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); - if (!m_DefaultSubscription) - m_DefaultSubscription = std::make_shared(*this, defaultSubURL); - m_IsDownloading = true; - std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription)); - load_hosts.detach(); // TODO: use join - } - else if (!m_Subscriptions.empty ()) - { - // pick random subscription - auto ind = rand () % m_Subscriptions.size(); - m_IsDownloading = true; - std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind])); - load_hosts.detach(); // TODO: use join - } - } - else - { - // try it again later - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - } - } + void AddressBook::HandleSubscriptionsUpdateTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto dest = i2p::client::context.GetSharedLocalDestination(); + if (!dest) { + LogPrint(eLogWarning, "Addressbook: Missing local destination, skip subscription update"); + return; + } + if (!m_IsDownloading && dest->IsReady()) { + if (!m_IsLoaded) { + // download it from default subscription + LogPrint(eLogInfo, "Addressbook: Trying to download it from default subscription."); + std::string defaultSubURL; + i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); + if (!m_DefaultSubscription) + m_DefaultSubscription = std::make_shared(*this, defaultSubURL); + m_IsDownloading = true; + std::thread + load_hosts(std::bind(&AddressBookSubscription::CheckUpdates, m_DefaultSubscription)); + load_hosts.detach(); // TODO: use join + } else if (!m_Subscriptions.empty()) { + // pick random subscription + auto ind = rand() % m_Subscriptions.size(); + m_IsDownloading = true; + std::thread + load_hosts(std::bind(&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind])); + load_hosts.detach(); // TODO: use join + } + } else { + // try it again later + m_SubscriptionsUpdateTimer->expires_from_now( + boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait(std::bind(&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + } + } - void AddressBook::StartLookups () - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - auto datagram = dest->GetDatagramDestination (); - if (!datagram) - datagram = dest->CreateDatagramDestination (); - datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - ADDRESS_RESPONSE_DATAGRAM_PORT); - } - } + void AddressBook::StartLookups() { + auto dest = i2p::client::context.GetSharedLocalDestination(); + if (dest) { + auto datagram = dest->GetDatagramDestination(); + if (!datagram) + datagram = dest->CreateDatagramDestination(); + datagram->SetReceiver(std::bind(&AddressBook::HandleLookupResponse, this, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5), + ADDRESS_RESPONSE_DATAGRAM_PORT); + } + } - void AddressBook::StopLookups () - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - auto datagram = dest->GetDatagramDestination (); - if (datagram) datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT); - } - } + void AddressBook::StopLookups() { + auto dest = i2p::client::context.GetSharedLocalDestination(); + if (dest) { + auto datagram = dest->GetDatagramDestination(); + if (datagram) datagram->ResetReceiver(ADDRESS_RESPONSE_DATAGRAM_PORT); + } + } - void AddressBook::LookupAddress (const std::string& address) - { - std::shared_ptr addr; - auto dot = address.find ('.'); - if (dot != std::string::npos) - addr = FindAddress (address.substr (dot + 1)); - if (!addr || !addr->IsIdentHash ()) // TODO: - { - LogPrint (eLogError, "Addressbook: Can't find domain for ", address); - return; - } + void AddressBook::LookupAddress(const std::string &address) { + std::shared_ptr addr; + auto dot = address.find('.'); + if (dot != std::string::npos) + addr = FindAddress(address.substr(dot + 1)); + if (!addr || !addr->IsIdentHash()) // TODO: + { + LogPrint(eLogError, "Addressbook: Can't find domain for ", address); + return; + } - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - auto datagram = dest->GetDatagramDestination (); - if (datagram) - { - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - { - std::unique_lock l(m_LookupsMutex); - m_Lookups[nonce] = address; - } - LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", addr->identHash.ToBase32 (), " nonce=", nonce); - size_t len = address.length () + 9; - uint8_t * buf = new uint8_t[len]; - memset (buf, 0, 4); - htobe32buf (buf + 4, nonce); - buf[8] = address.length (); - memcpy (buf + 9, address.c_str (), address.length ()); - datagram->SendDatagramTo (buf, len, addr->identHash, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT); - delete[] buf; - } - } - } + auto dest = i2p::client::context.GetSharedLocalDestination(); + if (dest) { + auto datagram = dest->GetDatagramDestination(); + if (datagram) { + uint32_t nonce; + RAND_bytes((uint8_t * ) & nonce, 4); + { + std::unique_lock l(m_LookupsMutex); + m_Lookups[nonce] = address; + } + LogPrint(eLogDebug, "Addressbook: Lookup of ", address, " to ", addr->identHash.ToBase32(), + " nonce=", nonce); + size_t len = address.length() + 9; + uint8_t *buf = new uint8_t[len]; + memset(buf, 0, 4); + htobe32buf(buf + 4, nonce); + buf[8] = address.length(); + memcpy(buf + 9, address.c_str(), address.length()); + datagram->SendDatagramTo(buf, len, addr->identHash, ADDRESS_RESPONSE_DATAGRAM_PORT, + ADDRESS_RESOLVER_DATAGRAM_PORT); + delete[] buf; + } + } + } - void AddressBook::HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (len < 44) - { - LogPrint (eLogError, "Addressbook: Lookup response is too short ", len); - return; - } - uint32_t nonce = bufbe32toh (buf + 4); - LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce); - std::string address; - { - std::unique_lock l(m_LookupsMutex); - auto it = m_Lookups.find (nonce); - if (it != m_Lookups.end ()) - { - address = it->second; - m_Lookups.erase (it); - } - } - if (address.length () > 0) - { - // TODO: verify from - i2p::data::IdentHash hash(buf + 8); - if (!hash.IsZero ()) - m_Addresses[address] = std::make_shared
(hash); - else - LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found"); - } - } + void AddressBook::HandleLookupResponse(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len) { + if (len < 44) { + LogPrint(eLogError, "Addressbook: Lookup response is too short ", len); + return; + } + uint32_t nonce = bufbe32toh(buf + 4); + LogPrint(eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash().ToBase32(), + " nonce=", nonce); + std::string address; + { + std::unique_lock l(m_LookupsMutex); + auto it = m_Lookups.find(nonce); + if (it != m_Lookups.end()) { + address = it->second; + m_Lookups.erase(it); + } + } + if (address.length() > 0) { + // TODO: verify from + i2p::data::IdentHash hash(buf + 8); + if (!hash.IsZero()) + m_Addresses[address] = std::make_shared
(hash); + else + LogPrint(eLogInfo, "AddressBook: Lookup response: ", address, " not found"); + } + } - AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link): - m_Book (book), m_Link (link) - { - } + AddressBookSubscription::AddressBookSubscription(AddressBook &book, const std::string &link) : + m_Book(book), m_Link(link) { + } - void AddressBookSubscription::CheckUpdates () - { - i2p::util::SetThreadName("Addressbook"); + void AddressBookSubscription::CheckUpdates() { + i2p::util::SetThreadName("Addressbook"); - bool result = MakeRequest (); - m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified); - } + bool result = MakeRequest(); + m_Book.DownloadComplete(result, m_Ident, m_Etag, m_LastModified); + } - bool AddressBookSubscription::MakeRequest () - { - i2p::http::URL url; - // must be run in separate thread - LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link); - if (!url.parse(m_Link)) - { - LogPrint(eLogError, "Addressbook: Failed to parse url: ", m_Link); - return false; - } - auto addr = m_Book.GetAddress (url.host); - if (!addr || !addr->IsIdentHash ()) - { - LogPrint (eLogError, "Addressbook: Can't resolve ", url.host); - return false; - } - else - m_Ident = addr->identHash; - /* this code block still needs some love */ - std::condition_variable newDataReceived; - std::mutex newDataReceivedMutex; - auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident); - if (!leaseSet) - { - std::unique_lock l(newDataReceivedMutex); - i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident, - [&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr ls) - { - leaseSet = ls; - std::unique_lock l1(newDataReceivedMutex); - newDataReceived.notify_all (); - }); - if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) - { - LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired"); - i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already - return false; - } - } - if (!leaseSet) { - /* still no leaseset found */ - LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found"); - return false; - } - if (m_Etag.empty() && m_LastModified.empty()) { - m_Book.GetEtag (m_Ident, m_Etag, m_LastModified); - LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified); - } - /* save url parts for later use */ - std::string dest_host = url.host; - int dest_port = url.port ? url.port : 80; - /* create http request & send it */ - i2p::http::HTTPReq req; - req.AddHeader("Host", dest_host); - req.AddHeader("User-Agent", "Wget/1.11.4"); - req.AddHeader("Accept-Encoding", "gzip"); - req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0"); - req.AddHeader("Connection", "close"); - if (!m_Etag.empty()) - req.AddHeader("If-None-Match", m_Etag); - if (!m_LastModified.empty()) - req.AddHeader("If-Modified-Since", m_LastModified); - /* convert url to relative */ - url.schema = ""; - url.host = ""; - req.uri = url.to_string(); - req.version = "HTTP/1.1"; - auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port); - std::string request = req.to_string(); - stream->Send ((const uint8_t *) request.data(), request.length()); - /* read response */ - std::string response; - uint8_t recv_buf[4096]; - bool end = false; - int numAttempts = 0; - while (!end) - { - stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096), - [&](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (bytes_transferred) - response.append ((char *)recv_buf, bytes_transferred); - if (ecode == boost::asio::error::timed_out || !stream->IsOpen ()) - end = true; - newDataReceived.notify_all (); - }, - SUBSCRIPTION_REQUEST_TIMEOUT); - std::unique_lock l(newDataReceivedMutex); - // wait 1 more second - if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT + 1)) == std::cv_status::timeout) - { - LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired"); - numAttempts++; - if (numAttempts > 5) end = true; - } - } - // process remaining buffer - while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) - response.append ((char *)recv_buf, len); - /* parse response */ - i2p::http::HTTPRes res; - int res_head_len = res.parse(response); - if (res_head_len < 0) - { - LogPrint(eLogError, "Addressbook: Can't parse http response from ", dest_host); - return false; - } - if (res_head_len == 0) - { - LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout"); - return false; - } - /* assert: res_head_len > 0 */ - response.erase(0, res_head_len); - if (res.code == 304) - { - LogPrint (eLogInfo, "Addressbook: No updates from ", dest_host, ", code 304"); - return false; - } - if (res.code != 200) - { - LogPrint (eLogWarning, "Adressbook: Can't get updates from ", dest_host, ", response code ", res.code); - return false; - } - int len = res.content_length(); - if (response.empty()) - { - LogPrint(eLogError, "Addressbook: Empty response from ", dest_host, ", expected ", len, " bytes"); - return false; - } - if (!res.is_gzipped () && len > 0 && len != (int) response.length()) - { - LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); - return false; - } - /* assert: res.code == 200 */ - auto it = res.headers.find("ETag"); - if (it != res.headers.end()) m_Etag = it->second; - it = res.headers.find("Last-Modified"); - if (it != res.headers.end()) m_LastModified = it->second; - if (res.is_chunked()) - { - std::stringstream in(response), out; - i2p::http::MergeChunkedResponse (in, out); - response = out.str(); - } - if (res.is_gzipped()) - { - std::stringstream out; - i2p::data::GzipInflator inflator; - inflator.Inflate ((const uint8_t *) response.data(), response.length(), out); - if (out.fail()) - { - LogPrint(eLogError, "Addressbook: Can't gunzip http response"); - return false; - } - response = out.str(); - } - std::stringstream ss(response); - LogPrint (eLogInfo, "Addressbook: Got update from ", dest_host); - m_Book.LoadHostsFromStream (ss, true); - return true; - } + bool AddressBookSubscription::MakeRequest() { + i2p::http::URL url; + // must be run in separate thread + LogPrint(eLogInfo, "Addressbook: Downloading hosts database from ", m_Link); + if (!url.parse(m_Link)) { + LogPrint(eLogError, "Addressbook: Failed to parse url: ", m_Link); + return false; + } + auto addr = m_Book.GetAddress(url.host); + if (!addr || !addr->IsIdentHash()) { + LogPrint(eLogError, "Addressbook: Can't resolve ", url.host); + return false; + } else + m_Ident = addr->identHash; + /* this code block still needs some love */ + std::condition_variable newDataReceived; + std::mutex newDataReceivedMutex; + auto leaseSet = i2p::client::context.GetSharedLocalDestination()->FindLeaseSet(m_Ident); + if (!leaseSet) { + std::unique_lock l(newDataReceivedMutex); + i2p::client::context.GetSharedLocalDestination()->RequestDestination(m_Ident, + [&newDataReceived, &leaseSet, &newDataReceivedMutex]( + std::shared_ptr ls) { + leaseSet = ls; + std::unique_lock l1( + newDataReceivedMutex); + newDataReceived.notify_all(); + }); + if (newDataReceived.wait_for(l, std::chrono::seconds(SUBSCRIPTION_REQUEST_TIMEOUT)) == + std::cv_status::timeout) { + LogPrint(eLogError, "Addressbook: Subscription LeaseSet request timeout expired"); + i2p::client::context.GetSharedLocalDestination()->CancelDestinationRequest(m_Ident, + false); // don't notify, because we know it already + return false; + } + } + if (!leaseSet) { + /* still no leaseset found */ + LogPrint(eLogError, "Addressbook: LeaseSet for address ", url.host, " not found"); + return false; + } + if (m_Etag.empty() && m_LastModified.empty()) { + m_Book.GetEtag(m_Ident, m_Etag, m_LastModified); + LogPrint(eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", + m_LastModified); + } + /* save url parts for later use */ + std::string dest_host = url.host; + int dest_port = url.port ? url.port : 80; + /* create http request & send it */ + i2p::http::HTTPReq req; + req.AddHeader("Host", dest_host); + req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("Accept-Encoding", "gzip"); + req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0"); + req.AddHeader("Connection", "close"); + if (!m_Etag.empty()) + req.AddHeader("If-None-Match", m_Etag); + if (!m_LastModified.empty()) + req.AddHeader("If-Modified-Since", m_LastModified); + /* convert url to relative */ + url.schema = ""; + url.host = ""; + req.uri = url.to_string(); + req.version = "HTTP/1.1"; + auto stream = i2p::client::context.GetSharedLocalDestination()->CreateStream(leaseSet, dest_port); + std::string request = req.to_string(); + stream->Send((const uint8_t *) request.data(), request.length()); + /* read response */ + std::string response; + uint8_t recv_buf[4096]; + bool end = false; + int numAttempts = 0; + while (!end) { + stream->AsyncReceive(boost::asio::buffer(recv_buf, 4096), + [&](const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (bytes_transferred) + response.append((char *) recv_buf, bytes_transferred); + if (ecode == boost::asio::error::timed_out || !stream->IsOpen()) + end = true; + newDataReceived.notify_all(); + }, + SUBSCRIPTION_REQUEST_TIMEOUT); + std::unique_lock l(newDataReceivedMutex); + // wait 1 more second + if (newDataReceived.wait_for(l, std::chrono::seconds(SUBSCRIPTION_REQUEST_TIMEOUT + 1)) == + std::cv_status::timeout) { + LogPrint(eLogError, "Addressbook: Subscriptions request timeout expired"); + numAttempts++; + if (numAttempts > 5) end = true; + } + } + // process remaining buffer + while (size_t len = stream->ReadSome(recv_buf, sizeof(recv_buf))) + response.append((char *) recv_buf, len); + /* parse response */ + i2p::http::HTTPRes res; + int res_head_len = res.parse(response); + if (res_head_len < 0) { + LogPrint(eLogError, "Addressbook: Can't parse http response from ", dest_host); + return false; + } + if (res_head_len == 0) { + LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, + ", interrupted by timeout"); + return false; + } + /* assert: res_head_len > 0 */ + response.erase(0, res_head_len); + if (res.code == 304) { + LogPrint(eLogInfo, "Addressbook: No updates from ", dest_host, ", code 304"); + return false; + } + if (res.code != 200) { + LogPrint(eLogWarning, "Adressbook: Can't get updates from ", dest_host, ", response code ", res.code); + return false; + } + int len = res.content_length(); + if (response.empty()) { + LogPrint(eLogError, "Addressbook: Empty response from ", dest_host, ", expected ", len, " bytes"); + return false; + } + if (!res.is_gzipped() && len > 0 && len != (int) response.length()) { + LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", + response.length(), "bytes"); + return false; + } + /* assert: res.code == 200 */ + auto it = res.headers.find("ETag"); + if (it != res.headers.end()) m_Etag = it->second; + it = res.headers.find("Last-Modified"); + if (it != res.headers.end()) m_LastModified = it->second; + if (res.is_chunked()) { + std::stringstream in(response), out; + i2p::http::MergeChunkedResponse(in, out); + response = out.str(); + } + if (res.is_gzipped()) { + std::stringstream out; + i2p::data::GzipInflator inflator; + inflator.Inflate((const uint8_t *) response.data(), response.length(), out); + if (out.fail()) { + LogPrint(eLogError, "Addressbook: Can't gunzip http response"); + return false; + } + response = out.str(); + } + std::stringstream ss(response); + LogPrint(eLogInfo, "Addressbook: Got update from ", dest_host); + m_Book.LoadHostsFromStream(ss, true); + return true; + } - AddressResolver::AddressResolver (std::shared_ptr destination): - m_LocalDestination (destination) - { - if (m_LocalDestination) - { - auto datagram = m_LocalDestination->GetDatagramDestination (); - if (!datagram) - datagram = m_LocalDestination->CreateDatagramDestination (); - datagram->SetReceiver (std::bind (&AddressResolver::HandleRequest, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - ADDRESS_RESOLVER_DATAGRAM_PORT); - } - } + AddressResolver::AddressResolver(std::shared_ptr destination) : + m_LocalDestination(destination) { + if (m_LocalDestination) { + auto datagram = m_LocalDestination->GetDatagramDestination(); + if (!datagram) + datagram = m_LocalDestination->CreateDatagramDestination(); + datagram->SetReceiver(std::bind(&AddressResolver::HandleRequest, this, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5), + ADDRESS_RESOLVER_DATAGRAM_PORT); + } + } - AddressResolver::~AddressResolver () - { - if (m_LocalDestination) - { - auto datagram = m_LocalDestination->GetDatagramDestination (); - if (datagram) - datagram->ResetReceiver (ADDRESS_RESOLVER_DATAGRAM_PORT); - } - } + AddressResolver::~AddressResolver() { + if (m_LocalDestination) { + auto datagram = m_LocalDestination->GetDatagramDestination(); + if (datagram) + datagram->ResetReceiver(ADDRESS_RESOLVER_DATAGRAM_PORT); + } + } - void AddressResolver::HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (len < 9 || len < buf[8] + 9U) - { - LogPrint (eLogError, "Addressbook: Address request is too short ", len); - return; - } - // read requested address - uint8_t l = buf[8]; - char address[255]; - memcpy (address, buf + 9, l); - address[l] = 0; - LogPrint (eLogDebug, "Addressbook: Address request ", address); - // send response - uint8_t response[44]; - memset (response, 0, 4); // reserved - memcpy (response + 4, buf + 4, 4); // nonce - auto it = m_LocalAddresses.find (address); // address lookup - if (it != m_LocalAddresses.end ()) - memcpy (response + 8, it->second, 32); // ident - else - memset (response + 8, 0, 32); // not found - memset (response + 40, 0, 4); // set expiration time to zero - m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort); - } + void AddressResolver::HandleRequest(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len) { + if (len < 9 || len < buf[8] + 9U) { + LogPrint(eLogError, "Addressbook: Address request is too short ", len); + return; + } + // read requested address + uint8_t l = buf[8]; + char address[255]; + memcpy(address, buf + 9, l); + address[l] = 0; + LogPrint(eLogDebug, "Addressbook: Address request ", address); + // send response + uint8_t response[44]; + memset(response, 0, 4); // reserved + memcpy(response + 4, buf + 4, 4); // nonce + auto it = m_LocalAddresses.find(address); // address lookup + if (it != m_LocalAddresses.end()) + memcpy(response + 8, it->second, 32); // ident + else + memset(response + 8, 0, 32); // not found + memset(response + 40, 0, 4); // set expiration time to zero + m_LocalDestination->GetDatagramDestination()->SendDatagramTo(response, 44, from.GetIdentHash(), toPort, + fromPort); + } - void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident) - { - m_LocalAddresses[name] = ident; - } + void AddressResolver::AddAddress(const std::string &name, const i2p::data::IdentHash &ident) { + m_LocalAddresses[name] = ident; + } -} + } } diff --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h index 192c4ebb..4eb084a1 100644 --- a/libi2pd_client/AddressBook.h +++ b/libi2pd_client/AddressBook.h @@ -23,149 +23,189 @@ #include "Destination.h" #include "LeaseSet.h" -namespace i2p -{ -namespace client -{ - const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes - const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes - const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) - const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes - const int CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES = 10; // then update timeout - const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in second +namespace i2p { + namespace client { + const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes + const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes + const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) + const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes + const int CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES = 10; // then update timeout + const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in second - const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; - const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54; + const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; + const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54; - const size_t B33_ADDRESS_THRESHOLD = 52; // characters + const size_t B33_ADDRESS_THRESHOLD = 52; // characters - struct Address - { - enum { eAddressIndentHash, eAddressBlindedPublicKey, eAddressInvalid } addressType; - i2p::data::IdentHash identHash; - std::shared_ptr blindedPublicKey; + struct Address { + enum { + eAddressIndentHash, eAddressBlindedPublicKey, eAddressInvalid + } addressType; + i2p::data::IdentHash identHash; + std::shared_ptr blindedPublicKey; - Address (const std::string& b32); - Address (const i2p::data::IdentHash& hash); - bool IsIdentHash () const { return addressType == eAddressIndentHash; }; - bool IsValid () const { return addressType != eAddressInvalid; }; - }; + Address(const std::string &b32); - inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); } + Address(const i2p::data::IdentHash &hash); - class AddressBookStorage // interface for storage - { - public: + bool IsIdentHash() const { return addressType == eAddressIndentHash; }; - virtual ~AddressBookStorage () {}; - virtual std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) const = 0; - virtual void AddAddress (std::shared_ptr address) = 0; - virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; + bool IsValid() const { return addressType != eAddressInvalid; }; + }; - virtual bool Init () = 0; - virtual int Load (std::map >& addresses) = 0; - virtual int LoadLocal (std::map >& addresses) = 0; - virtual int Save (const std::map >& addresses) = 0; + inline std::string GetB32Address(const i2p::data::IdentHash &ident) { + return ident.ToBase32().append(".b32.i2p"); + } - virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0; - virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0; - virtual void ResetEtags () = 0; - }; + class AddressBookStorage // interface for storage + { + public: - class AddressBookSubscription; - class AddressResolver; - class AddressBook - { - public: + virtual ~AddressBookStorage() {}; - AddressBook (); - ~AddressBook (); - void Start (); - void StartResolvers (); - void Stop (); - std::shared_ptr GetAddress (const std::string& address); - std::shared_ptr GetFullAddress (const std::string& address); - std::shared_ptr FindAddress (const std::string& address); - void LookupAddress (const std::string& address); - void InsertAddress (const std::string& address, const std::string& jump); // for jump links - void InsertFullAddress (std::shared_ptr address); + virtual std::shared_ptr + GetAddress(const i2p::data::IdentHash &ident) const = 0; - bool LoadHostsFromStream (std::istream& f, bool is_update); - void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified); - //This method returns the ".b32.i2p" address - std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); } - std::string ToAddress(std::shared_ptr ident) { return ToAddress(ident->GetIdentHash ()); } + virtual void AddAddress(std::shared_ptr address) = 0; - bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified); + virtual void RemoveAddress(const i2p::data::IdentHash &ident) = 0; - private: + virtual bool Init() = 0; - void StartSubscriptions (); - void StopSubscriptions (); + virtual int Load(std::map > &addresses) = 0; - void LoadHosts (); - void LoadSubscriptions (); - void LoadLocal (); + virtual int LoadLocal(std::map > &addresses) = 0; - void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); + virtual int Save(const std::map > &addresses) = 0; - void StartLookups (); - void StopLookups (); - void HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + virtual void SaveEtag(const i2p::data::IdentHash &subscription, const std::string &etag, + const std::string &lastModified) = 0; - private: + virtual bool + GetEtag(const i2p::data::IdentHash &subscription, std::string &etag, std::string &lastModified) = 0; - std::mutex m_AddressBookMutex; - std::map > m_Addresses; - std::map > m_Resolvers; // local destination->resolver - std::mutex m_LookupsMutex; - std::map m_Lookups; // nonce -> address - AddressBookStorage * m_Storage; - volatile bool m_IsLoaded, m_IsDownloading; - int m_NumRetries; - std::vector > m_Subscriptions; - std::shared_ptr m_DefaultSubscription; // in case if we don't know any addresses yet - boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; - bool m_IsEnabled; - }; + virtual void ResetEtags() = 0; + }; - class AddressBookSubscription - { - public: + class AddressBookSubscription; - AddressBookSubscription (AddressBook& book, const std::string& link); - void CheckUpdates (); + class AddressResolver; - private: + class AddressBook { + public: - bool MakeRequest (); + AddressBook(); - private: + ~AddressBook(); - AddressBook& m_Book; - std::string m_Link, m_Etag, m_LastModified; - i2p::data::IdentHash m_Ident; - // m_Etag must be surrounded by "" - }; + void Start(); - class AddressResolver - { - public: + void StartResolvers(); - AddressResolver (std::shared_ptr destination); - ~AddressResolver (); - void AddAddress (const std::string& name, const i2p::data::IdentHash& ident); + void Stop(); - private: + std::shared_ptr GetAddress(const std::string &address); - void HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + std::shared_ptr GetFullAddress(const std::string &address); - private: + std::shared_ptr FindAddress(const std::string &address); - std::shared_ptr m_LocalDestination; - std::map m_LocalAddresses; - }; -} + void LookupAddress(const std::string &address); + + void InsertAddress(const std::string &address, const std::string &jump); // for jump links + void InsertFullAddress(std::shared_ptr address); + + bool LoadHostsFromStream(std::istream &f, bool is_update); + + void DownloadComplete(bool success, const i2p::data::IdentHash &subscription, const std::string &etag, + const std::string &lastModified); + + //This method returns the ".b32.i2p" address + std::string ToAddress(const i2p::data::IdentHash &ident) { return GetB32Address(ident); } + + std::string ToAddress(std::shared_ptr ident) { + return ToAddress(ident->GetIdentHash()); + } + + bool GetEtag(const i2p::data::IdentHash &subscription, std::string &etag, std::string &lastModified); + + private: + + void StartSubscriptions(); + + void StopSubscriptions(); + + void LoadHosts(); + + void LoadSubscriptions(); + + void LoadLocal(); + + void HandleSubscriptionsUpdateTimer(const boost::system::error_code &ecode); + + void StartLookups(); + + void StopLookups(); + + void HandleLookupResponse(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len); + + private: + + std::mutex m_AddressBookMutex; + std::map > m_Addresses; + std::map > m_Resolvers; // local destination->resolver + std::mutex m_LookupsMutex; + std::map m_Lookups; // nonce -> address + AddressBookStorage *m_Storage; + volatile bool m_IsLoaded, m_IsDownloading; + int m_NumRetries; + std::vector > m_Subscriptions; + std::shared_ptr m_DefaultSubscription; // in case if we don't know any addresses yet + boost::asio::deadline_timer *m_SubscriptionsUpdateTimer; + bool m_IsEnabled; + }; + + class AddressBookSubscription { + public: + + AddressBookSubscription(AddressBook &book, const std::string &link); + + void CheckUpdates(); + + private: + + bool MakeRequest(); + + private: + + AddressBook &m_Book; + std::string m_Link, m_Etag, m_LastModified; + i2p::data::IdentHash m_Ident; + // m_Etag must be surrounded by "" + }; + + class AddressResolver { + public: + + AddressResolver(std::shared_ptr destination); + + ~AddressResolver(); + + void AddAddress(const std::string &name, const i2p::data::IdentHash &ident); + + private: + + void + HandleRequest(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, const uint8_t *buf, + size_t len); + + private: + + std::shared_ptr m_LocalDestination; + std::map m_LocalAddresses; + }; + } } #endif diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index a8520e9b..17c9c117 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -12,924 +12,798 @@ #include "util.h" #include "BOB.h" -namespace i2p -{ -namespace client -{ - BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr localDestination): - BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep) - { - } - - BOBI2PInboundTunnel::~BOBI2PInboundTunnel () - { - Stop (); - } - - void BOBI2PInboundTunnel::Start () - { - m_Acceptor.listen (); - Accept (); - } - - void BOBI2PInboundTunnel::Stop () - { - m_Acceptor.close(); - ClearHandlers (); - } - - void BOBI2PInboundTunnel::Accept () - { - auto receiver = std::make_shared (); - receiver->socket = std::make_shared (GetService ()); - m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this, - std::placeholders::_1, receiver)); - } - - void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver) - { - if (!ecode) - { - Accept (); - ReceiveAddress (receiver); - } - } - - void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr receiver) - { - receiver->socket->async_read_some (boost::asio::buffer( - receiver->buffer + receiver->bufferOffset, - BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), - std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, - std::placeholders::_1, std::placeholders::_2, receiver)); - } - - void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr receiver) - { - if (ecode) - LogPrint (eLogError, "BOB: Inbound tunnel read error: ", ecode.message ()); - else - { - receiver->bufferOffset += bytes_transferred; - receiver->buffer[receiver->bufferOffset] = 0; - char * eol = strchr (receiver->buffer, '\n'); - if (eol) - { - *eol = 0; - if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address - receiver->data = (uint8_t *)eol + 1; - receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); - auto addr = context.GetAddressBook ().GetAddress (receiver->buffer); - if (!addr) - { - LogPrint (eLogError, "BOB: Address ", receiver->buffer, " not found"); - return; - } - if (addr->IsIdentHash ()) - { - auto leaseSet = GetLocalDestination ()->FindLeaseSet (addr->identHash); - if (leaseSet) - CreateConnection (receiver, leaseSet); - else - GetLocalDestination ()->RequestDestination (addr->identHash, - std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, - this, std::placeholders::_1, receiver)); - } - else - GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, - std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, - this, std::placeholders::_1, receiver)); - } - else - { - if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) - ReceiveAddress (receiver); - else - LogPrint (eLogError, "BOB: Missing inbound address"); - } - } - } - - void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver) - { - if (leaseSet) - CreateConnection (receiver, leaseSet); - else - LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found"); - } - - void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet) - { - LogPrint (eLogDebug, "BOB: New inbound connection"); - auto connection = std::make_shared(this, receiver->socket, leaseSet); - AddHandler (connection); - connection->I2PConnect (receiver->data, receiver->dataLen); - } - - BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, int port, - std::shared_ptr localDestination, bool quiet): BOBI2PTunnel (localDestination), - m_Endpoint (boost::asio::ip::address::from_string (outhost), port), m_IsQuiet (quiet) - { - } - - void BOBI2POutboundTunnel::Start () - { - Accept (); - } - - void BOBI2POutboundTunnel::Stop () - { - ClearHandlers (); - } - - void BOBI2POutboundTunnel::Accept () - { - auto localDestination = GetLocalDestination (); - if (localDestination) - localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1)); - else - LogPrint (eLogError, "BOB: Local destination not set for server tunnel"); - } - - void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr stream) - { - if (stream) - { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), m_Endpoint, m_IsQuiet); - AddHandler (conn); - conn->Connect (); - } - } - - BOBDestination::BOBDestination (std::shared_ptr localDestination, - const std::string &nickname, const std::string &inhost, const std::string &outhost, - const int inport, const int outport, const bool quiet): - m_LocalDestination (localDestination), - m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr), - m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost), - m_InPort(inport), m_OutPort(outport), m_Quiet(quiet), m_IsRunning(false) - { - } - - BOBDestination::~BOBDestination () - { - delete m_OutboundTunnel; - delete m_InboundTunnel; - i2p::client::context.DeleteLocalDestination (m_LocalDestination); - } - - void BOBDestination::Start () - { - if (m_OutboundTunnel) m_OutboundTunnel->Start (); - if (m_InboundTunnel) m_InboundTunnel->Start (); - m_IsRunning = true; - } - - void BOBDestination::Stop () - { - StopTunnels (); - m_LocalDestination->Stop (); - } - - void BOBDestination::StopTunnels () - { - m_IsRunning = false; - if (m_OutboundTunnel) - { - m_OutboundTunnel->Stop (); - delete m_OutboundTunnel; - m_OutboundTunnel = nullptr; - } - if (m_InboundTunnel) - { - m_InboundTunnel->Stop (); - delete m_InboundTunnel; - m_InboundTunnel = nullptr; - } - } - - void BOBDestination::CreateInboundTunnel (int port, const std::string& inhost) - { - if (!m_InboundTunnel) - { - // update inport and inhost (user can stop tunnel and change) - m_InPort = port; - m_InHost = inhost; - boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), port); - if (!inhost.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string (inhost, ec); - if (!ec) - ep.address (addr); - else - LogPrint (eLogError, "BOB: ", ec.message ()); - } - m_InboundTunnel = new BOBI2PInboundTunnel (ep, m_LocalDestination); - } - } - - void BOBDestination::CreateOutboundTunnel (const std::string& outhost, int port, bool quiet) - { - if (!m_OutboundTunnel) - { - // update outport and outhost (user can stop tunnel and change) - m_OutPort = port; - m_OutHost = outhost; - m_OutboundTunnel = new BOBI2POutboundTunnel (outhost, port, m_LocalDestination, quiet); - } - } - - BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): - m_Owner (owner), m_Socket (m_Owner.GetService ()), - m_ReceiveBuffer(BOB_COMMAND_BUFFER_SIZE + 1), m_SendBuffer(BOB_COMMAND_BUFFER_SIZE + 1), - m_IsOpen (true), m_IsQuiet (false), m_IsActive (false), - m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr) - { - } - - BOBCommandSession::~BOBCommandSession () - { - } - - void BOBCommandSession::Terminate () - { - m_Socket.close (); - m_IsOpen = false; - } - - void BOBCommandSession::Receive () - { - boost::asio::async_read_until(m_Socket, m_ReceiveBuffer, '\n', - std::bind(&BOBCommandSession::HandleReceivedLine, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - - void BOBCommandSession::HandleReceivedLine(const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if(ecode) - { - LogPrint (eLogError, "BOB: Command channel read error: ", ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - std::string line; - - std::istream is(&m_ReceiveBuffer); - std::getline(is, line); - - std::string command, operand; - std::istringstream iss(line); - iss >> command >> operand; - - // process command - auto& handlers = m_Owner.GetCommandHandlers(); - auto it = handlers.find(command); - if(it != handlers.end()) - { - (this->*(it->second))(operand.c_str(), operand.length()); - } - else - { - LogPrint (eLogError, "BOB: Unknown command ", command.c_str()); - SendReplyError ("unknown command"); - } - } - } - - void BOBCommandSession::Send () - { - boost::asio::async_write (m_Socket, m_SendBuffer, - boost::asio::transfer_all (), - std::bind(&BOBCommandSession::HandleSent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "BOB: Command channel send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - if (m_IsOpen) - Receive (); - else - Terminate (); - } - } - - void BOBCommandSession::SendReplyOK (const char * msg) - { - std::ostream os(&m_SendBuffer); - os << "OK"; - if(msg) - { - os << " " << msg; - } - os << std::endl; - Send (); - } - - void BOBCommandSession::SendReplyError (const char * msg) - { - std::ostream os(&m_SendBuffer); - os << "ERROR " << msg << std::endl; - Send (); - } - - void BOBCommandSession::SendVersion () - { - std::ostream os(&m_SendBuffer); - os << "BOB 00.00.10" << std::endl; - SendReplyOK(); - } - - void BOBCommandSession::SendRaw (const char * data) - { - std::ostream os(&m_SendBuffer); - os << data << std::endl; - } - - void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out) - { - // helper lambdas - const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost - const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport - const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; }; - const auto destReady = [](const BOBDestination * const dest) { return dest->IsRunning(); }; - const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str - - // tunnel info - const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname(); - const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet(); - const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost()); - const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost()); - const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort()); - const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort()); - const bool keys = destExists(dest); // key must exist when destination is created - const bool starting = destExists(dest) && !destReady(dest); - const bool running = destExists(dest) && destReady(dest); - const bool stopping = false; - - // build line - std::stringstream ss; - ss << "DATA " - << "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " " - << "RUNNING: " << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " " - << "KEYS: " << bool_str(keys) << " " << "QUIET: " << bool_str(quiet) << " " - << "INPORT: " << inport << " " << "INHOST: " << inhost << " " - << "OUTPORT: " << outport << " " << "OUTHOST: " << outhost; - out = ss.str(); - } - - void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: zap"); - Terminate (); - } - - void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: quit"); - m_IsOpen = false; - SendReplyOK ("Bye!"); - } - - void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: start ", m_Nickname); - if (m_IsActive) - { - SendReplyError ("tunnel is active"); - return; - } - if (!m_Keys.GetPublic ()) // keys are set ? - { - SendReplyError("Keys must be set."); - return; - } - if (m_InPort == 0 - && m_OutHost.empty() && m_OutPort == 0) - { - SendReplyError("(inhost):inport or outhost:outport must be set."); - return; - } - if(!m_InHost.empty()) - { - // TODO: FIXME: temporary validation, until hostname support is added - boost::system::error_code ec; - boost::asio::ip::address::from_string(m_InHost, ec); - if (ec) - { - SendReplyError("inhost must be a valid IPv4 address."); - return; - } - } - if(!m_OutHost.empty()) - { - // TODO: FIXME: temporary validation, until hostname support is added - boost::system::error_code ec; - boost::asio::ip::address::from_string(m_OutHost, ec); - if (ec) - { - SendReplyError("outhost must be a IPv4 address."); - return; - } - } - - if (!m_CurrentDestination) - { - m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options), // deleted in clear command - m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, m_IsQuiet); - m_Owner.AddDestination (m_Nickname, m_CurrentDestination); - } - if (m_InPort) - m_CurrentDestination->CreateInboundTunnel (m_InPort, m_InHost); - if (m_OutPort && !m_OutHost.empty ()) - m_CurrentDestination->CreateOutboundTunnel (m_OutHost, m_OutPort, m_IsQuiet); - m_CurrentDestination->Start (); - SendReplyOK ("Tunnel starting"); - m_IsActive = true; - } - - void BOBCommandSession::StopCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: stop ", m_Nickname); - if (!m_IsActive) - { - SendReplyError ("tunnel is inactive"); - return; - } - auto dest = m_Owner.FindDestination (m_Nickname); - if (dest) - { - dest->StopTunnels (); - SendReplyOK ("Tunnel stopping"); - } - else - SendReplyError ("tunnel not found"); - m_IsActive = false; - } - - void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: setnick ", operand); - if(*operand) - { - auto dest = m_Owner.FindDestination (operand); - if (!dest) - { - m_Nickname = operand; - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("tunnel is active"); - } - else - SendReplyError ("no nickname has been set"); - } - - void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getnick ", operand); - if(*operand) - { - m_CurrentDestination = m_Owner.FindDestination (operand); - if (m_CurrentDestination) - { - m_Keys = m_CurrentDestination->GetKeys (); - m_IsActive = m_CurrentDestination->IsRunning (); - m_Nickname = operand; - } - if (m_Nickname == operand) - { - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("no nickname has been set"); - } - else - SendReplyError ("no nickname has been set"); - } - - void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: newkeys"); - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - if (*operand) - { - try - { - char * operand1 = (char *)strchr (operand, ' '); - if (operand1) - { - *operand1 = 0; operand1++; - cryptoType = std::stoi(operand1); - } - signatureType = std::stoi(operand); - } - catch (std::invalid_argument& ex) - { - LogPrint (eLogWarning, "BOB: Error on newkeys: ", ex.what ()); - } - } - - - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType); - SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); - } - - void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: setkeys ", operand); - if (*operand && m_Keys.FromBase64 (operand)) - SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); - else - SendReplyError ("invalid keys"); - } - - void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getkeys"); - if (m_Keys.GetPublic ()) // keys are set ? - SendReplyOK (m_Keys.ToBase64 ().c_str ()); - else - SendReplyError ("keys are not set"); - } - - void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getdest"); - if (m_Keys.GetPublic ()) // keys are set ? - SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); - else - SendReplyError ("keys are not set"); - } - - void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: outhost ", operand); - if (*operand) - { - m_OutHost = operand; - SendReplyOK ("outhost set"); - } - else - SendReplyError ("empty outhost"); - } - - void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: outport ", operand); - if (*operand) - { - m_OutPort = std::stoi(operand); - if (m_OutPort >= 0) - SendReplyOK ("outbound port set"); - else - SendReplyError ("port out of range"); - } - else - SendReplyError ("empty outport"); - } - - void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: inhost ", operand); - if (*operand) - { - m_InHost = operand; - SendReplyOK ("inhost set"); - } - else - SendReplyError ("empty inhost"); - } - - void BOBCommandSession::InportCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: inport ", operand); - if (*operand) - { - m_InPort = std::stoi(operand); - if (m_InPort >= 0) - SendReplyOK ("inbound port set"); - else - SendReplyError ("port out of range"); - } - else - SendReplyError ("empty inport"); - } - - void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: quiet"); - if (m_Nickname.length () > 0) - { - if (!m_IsActive) - { - m_IsQuiet = true; - SendReplyOK ("Quiet set"); - } - else - SendReplyError ("tunnel is active"); - } - else - SendReplyError ("no nickname has been set"); - } - - void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: lookup ", operand); - if (*operand) - { - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) - { - SendReplyError ("Address Not found"); - return; - } - auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); - if (addr->IsIdentHash ()) - { - // we might have leaseset already - auto leaseSet = localDestination->FindLeaseSet (addr->identHash); - if (leaseSet) - { - SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ()); - return; - } - } - // trying to request - auto s = shared_from_this (); - auto requstCallback = [s](std::shared_ptr ls) - { - if (ls) - s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); - else - s->SendReplyError ("LeaseSet Not found"); - }; - if (addr->IsIdentHash ()) - localDestination->RequestDestination (addr->identHash, requstCallback); - else - localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback); - } - else - SendReplyError ("empty lookup address"); - } - - void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: lookup local ", operand); - if (*operand) - { - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) - { - SendReplyError ("Address Not found"); - return; - } - auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash); - if (ls) - SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); - else - SendReplyError ("Local LeaseSet Not found"); - } - else - SendReplyError ("empty lookup address"); - } - - void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: clear"); - m_Owner.DeleteDestination (m_Nickname); - m_Nickname = ""; - SendReplyOK ("cleared"); - } - - void BOBCommandSession::ListCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: list"); - std::string statusLine; - bool sentCurrent = false; - const auto& destinations = m_Owner.GetDestinations (); - for (const auto& it: destinations) - { - BuildStatusLine(false, it.second, statusLine); - SendRaw(statusLine.c_str()); - if(m_Nickname.compare(it.second->GetNickname()) == 0) - sentCurrent = true; - } - if(!sentCurrent && !m_Nickname.empty()) - { - // add the current tunnel to the list. - // this is for the incomplete tunnel which has not been started yet. - BuildStatusLine(true, m_CurrentDestination, statusLine); - SendRaw(statusLine.c_str()); - } - SendReplyOK ("Listing done"); - } - - void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: option ", operand); - const char * value = strchr (operand, '='); - if (value) - { - std::string msg ("option "); - *(const_cast(value)) = 0; - m_Options[operand] = value + 1; - msg += operand; - *(const_cast(value)) = '='; - msg += " set to "; - msg += value + 1; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("malformed"); - } - - void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: status ", operand); - const std::string name = operand; - std::string statusLine; - - // always prefer destination - auto dest = m_Owner.FindDestination(name); - if(dest) - { - // tunnel destination exists - BuildStatusLine(false, dest, statusLine); - SendReplyOK(statusLine.c_str()); - } - else - { - if(m_Nickname == name && !name.empty()) - { - // tunnel is incomplete / has not been started yet - BuildStatusLine(true, nullptr, statusLine); - SendReplyOK(statusLine.c_str()); - } - else - { - SendReplyError("no nickname has been set"); - } - } - } - void BOBCommandSession::HelpCommandHandler (const char * operand, size_t len) - { - auto helpStrings = m_Owner.GetHelpStrings(); - if(!*operand) - { - std::stringstream ss; - ss << "COMMANDS:"; - for (auto const& x : helpStrings) - { - ss << " " << x.first; - } - const std::string &str = ss.str(); - SendReplyOK(str.c_str()); - } - else - { - auto it = helpStrings.find(operand); - if (it != helpStrings.end ()) - { - SendReplyOK(it->second.c_str()); - return; - } - SendReplyError("No such command"); - } - } - - BOBCommandChannel::BOBCommandChannel (const std::string& address, int port): - RunnableService ("BOB"), - m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) - { - // command -> handler - m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; - m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler; - m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler; - m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler; - m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler; - m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler; - m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler; - m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler; - m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler; - m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; - m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; - m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; - m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler; - m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; - m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; - m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; - m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler; - m_CommandHandlers[BOB_COMMAND_HELP] = &BOBCommandSession::HelpCommandHandler; - // command -> help string - m_HelpStrings[BOB_COMMAND_ZAP] = BOB_HELP_ZAP; - m_HelpStrings[BOB_COMMAND_QUIT] = BOB_HELP_QUIT; - m_HelpStrings[BOB_COMMAND_START] = BOB_HELP_START; - m_HelpStrings[BOB_COMMAND_STOP] = BOB_HELP_STOP; - m_HelpStrings[BOB_COMMAND_SETNICK] = BOB_HELP_SETNICK; - m_HelpStrings[BOB_COMMAND_GETNICK] = BOB_HELP_GETNICK; - m_HelpStrings[BOB_COMMAND_NEWKEYS] = BOB_HELP_NEWKEYS; - m_HelpStrings[BOB_COMMAND_GETKEYS] = BOB_HELP_GETKEYS; - m_HelpStrings[BOB_COMMAND_SETKEYS] = BOB_HELP_SETKEYS; - m_HelpStrings[BOB_COMMAND_GETDEST] = BOB_HELP_GETDEST; - m_HelpStrings[BOB_COMMAND_OUTHOST] = BOB_HELP_OUTHOST; - m_HelpStrings[BOB_COMMAND_OUTPORT] = BOB_HELP_OUTPORT; - m_HelpStrings[BOB_COMMAND_INHOST] = BOB_HELP_INHOST; - m_HelpStrings[BOB_COMMAND_INPORT] = BOB_HELP_INPORT; - m_HelpStrings[BOB_COMMAND_QUIET] = BOB_HELP_QUIET; - m_HelpStrings[BOB_COMMAND_LOOKUP] = BOB_HELP_LOOKUP; - m_HelpStrings[BOB_COMMAND_CLEAR] = BOB_HELP_CLEAR; - m_HelpStrings[BOB_COMMAND_LIST] = BOB_HELP_LIST; - m_HelpStrings[BOB_COMMAND_OPTION] = BOB_HELP_OPTION; - m_HelpStrings[BOB_COMMAND_STATUS] = BOB_HELP_STATUS; - m_HelpStrings[BOB_COMMAND_HELP] = BOB_HELP_HELP; - } - - BOBCommandChannel::~BOBCommandChannel () - { - if (IsRunning ()) - Stop (); - for (const auto& it: m_Destinations) - delete it.second; - } - - void BOBCommandChannel::Start () - { - Accept (); - StartIOService (); - } - - void BOBCommandChannel::Stop () - { - for (auto& it: m_Destinations) - it.second->Stop (); - m_Acceptor.cancel (); - StopIOService (); - } - - void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) - { - m_Destinations[name] = dest; - } - - void BOBCommandChannel::DeleteDestination (const std::string& name) - { - auto it = m_Destinations.find (name); - if (it != m_Destinations.end ()) - { - it->second->Stop (); - delete it->second; - m_Destinations.erase (it); - } - } - - BOBDestination * BOBCommandChannel::FindDestination (const std::string& name) - { - auto it = m_Destinations.find (name); - if (it != m_Destinations.end ()) - return it->second; - return nullptr; - } - - void BOBCommandChannel::Accept () - { - auto newSession = std::make_shared (*this); - m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this, - std::placeholders::_1, newSession)); - } - - void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session) - { - if (ecode != boost::asio::error::operation_aborted) - Accept (); - - if (!ecode) - { - LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ()); - session->SendVersion (); - } - else - LogPrint (eLogError, "BOB: Accept error: ", ecode.message ()); - } -} +namespace i2p { + namespace client { + BOBI2PInboundTunnel::BOBI2PInboundTunnel(const boost::asio::ip::tcp::endpoint &ep, + std::shared_ptr localDestination) : + BOBI2PTunnel(localDestination), m_Acceptor(localDestination->GetService(), ep) { + } + + BOBI2PInboundTunnel::~BOBI2PInboundTunnel() { + Stop(); + } + + void BOBI2PInboundTunnel::Start() { + m_Acceptor.listen(); + Accept(); + } + + void BOBI2PInboundTunnel::Stop() { + m_Acceptor.close(); + ClearHandlers(); + } + + void BOBI2PInboundTunnel::Accept() { + auto receiver = std::make_shared(); + receiver->socket = std::make_shared(GetService()); + m_Acceptor.async_accept(*receiver->socket, std::bind(&BOBI2PInboundTunnel::HandleAccept, this, + std::placeholders::_1, receiver)); + } + + void BOBI2PInboundTunnel::HandleAccept(const boost::system::error_code &ecode, + std::shared_ptr receiver) { + if (!ecode) { + Accept(); + ReceiveAddress(receiver); + } + } + + void BOBI2PInboundTunnel::ReceiveAddress(std::shared_ptr receiver) { + receiver->socket->async_read_some(boost::asio::buffer( + receiver->buffer + receiver->bufferOffset, + BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), + std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, + std::placeholders::_1, std::placeholders::_2, receiver)); + } + + void BOBI2PInboundTunnel::HandleReceivedAddress(const boost::system::error_code &ecode, + std::size_t bytes_transferred, + std::shared_ptr receiver) { + if (ecode) + LogPrint(eLogError, "BOB: Inbound tunnel read error: ", ecode.message()); + else { + receiver->bufferOffset += bytes_transferred; + receiver->buffer[receiver->bufferOffset] = 0; + char *eol = strchr(receiver->buffer, '\n'); + if (eol) { + *eol = 0; + if (eol != receiver->buffer && eol[-1] == '\r') + eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address + receiver->data = (uint8_t *) eol + 1; + receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); + auto addr = context.GetAddressBook().GetAddress(receiver->buffer); + if (!addr) { + LogPrint(eLogError, "BOB: Address ", receiver->buffer, " not found"); + return; + } + if (addr->IsIdentHash()) { + auto leaseSet = GetLocalDestination()->FindLeaseSet(addr->identHash); + if (leaseSet) + CreateConnection(receiver, leaseSet); + else + GetLocalDestination()->RequestDestination(addr->identHash, + std::bind( + &BOBI2PInboundTunnel::HandleDestinationRequestComplete, + this, std::placeholders::_1, receiver)); + } else + GetLocalDestination()->RequestDestinationWithEncryptedLeaseSet(addr->blindedPublicKey, + std::bind( + &BOBI2PInboundTunnel::HandleDestinationRequestComplete, + this, + std::placeholders::_1, + receiver)); + } else { + if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) + ReceiveAddress(receiver); + else + LogPrint(eLogError, "BOB: Missing inbound address"); + } + } + } + + void BOBI2PInboundTunnel::HandleDestinationRequestComplete(std::shared_ptr leaseSet, + std::shared_ptr receiver) { + if (leaseSet) + CreateConnection(receiver, leaseSet); + else + LogPrint(eLogError, "BOB: LeaseSet for inbound destination not found"); + } + + void BOBI2PInboundTunnel::CreateConnection(std::shared_ptr receiver, + std::shared_ptr leaseSet) { + LogPrint(eLogDebug, "BOB: New inbound connection"); + auto connection = std::make_shared(this, receiver->socket, leaseSet); + AddHandler(connection); + connection->I2PConnect(receiver->data, receiver->dataLen); + } + + BOBI2POutboundTunnel::BOBI2POutboundTunnel(const std::string &outhost, int port, + std::shared_ptr localDestination, bool quiet) + : BOBI2PTunnel(localDestination), + m_Endpoint(boost::asio::ip::address::from_string(outhost), port), m_IsQuiet(quiet) { + } + + void BOBI2POutboundTunnel::Start() { + Accept(); + } + + void BOBI2POutboundTunnel::Stop() { + ClearHandlers(); + } + + void BOBI2POutboundTunnel::Accept() { + auto localDestination = GetLocalDestination(); + if (localDestination) + localDestination->AcceptStreams( + std::bind(&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1)); + else + LogPrint(eLogError, "BOB: Local destination not set for server tunnel"); + } + + void BOBI2POutboundTunnel::HandleAccept(std::shared_ptr stream) { + if (stream) { + auto conn = std::make_shared(this, stream, + std::make_shared( + GetService()), m_Endpoint, m_IsQuiet); + AddHandler(conn); + conn->Connect(); + } + } + + BOBDestination::BOBDestination(std::shared_ptr localDestination, + const std::string &nickname, const std::string &inhost, + const std::string &outhost, + const int inport, const int outport, const bool quiet) : + m_LocalDestination(localDestination), + m_OutboundTunnel(nullptr), m_InboundTunnel(nullptr), + m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost), + m_InPort(inport), m_OutPort(outport), m_Quiet(quiet), m_IsRunning(false) { + } + + BOBDestination::~BOBDestination() { + delete m_OutboundTunnel; + delete m_InboundTunnel; + i2p::client::context.DeleteLocalDestination(m_LocalDestination); + } + + void BOBDestination::Start() { + if (m_OutboundTunnel) m_OutboundTunnel->Start(); + if (m_InboundTunnel) m_InboundTunnel->Start(); + m_IsRunning = true; + } + + void BOBDestination::Stop() { + StopTunnels(); + m_LocalDestination->Stop(); + } + + void BOBDestination::StopTunnels() { + m_IsRunning = false; + if (m_OutboundTunnel) { + m_OutboundTunnel->Stop(); + delete m_OutboundTunnel; + m_OutboundTunnel = nullptr; + } + if (m_InboundTunnel) { + m_InboundTunnel->Stop(); + delete m_InboundTunnel; + m_InboundTunnel = nullptr; + } + } + + void BOBDestination::CreateInboundTunnel(int port, const std::string &inhost) { + if (!m_InboundTunnel) { + // update inport and inhost (user can stop tunnel and change) + m_InPort = port; + m_InHost = inhost; + boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), port); + if (!inhost.empty()) { + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string(inhost, ec); + if (!ec) + ep.address(addr); + else + LogPrint(eLogError, "BOB: ", ec.message()); + } + m_InboundTunnel = new BOBI2PInboundTunnel(ep, m_LocalDestination); + } + } + + void BOBDestination::CreateOutboundTunnel(const std::string &outhost, int port, bool quiet) { + if (!m_OutboundTunnel) { + // update outport and outhost (user can stop tunnel and change) + m_OutPort = port; + m_OutHost = outhost; + m_OutboundTunnel = new BOBI2POutboundTunnel(outhost, port, m_LocalDestination, quiet); + } + } + + BOBCommandSession::BOBCommandSession(BOBCommandChannel &owner) : + m_Owner(owner), m_Socket(m_Owner.GetService()), + m_ReceiveBuffer(BOB_COMMAND_BUFFER_SIZE + 1), m_SendBuffer(BOB_COMMAND_BUFFER_SIZE + 1), + m_IsOpen(true), m_IsQuiet(false), m_IsActive(false), + m_InPort(0), m_OutPort(0), m_CurrentDestination(nullptr) { + } + + BOBCommandSession::~BOBCommandSession() { + } + + void BOBCommandSession::Terminate() { + m_Socket.close(); + m_IsOpen = false; + } + + void BOBCommandSession::Receive() { + boost::asio::async_read_until(m_Socket, m_ReceiveBuffer, '\n', + std::bind(&BOBCommandSession::HandleReceivedLine, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + + void + BOBCommandSession::HandleReceivedLine(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "BOB: Command channel read error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else { + std::string line; + + std::istream is(&m_ReceiveBuffer); + std::getline(is, line); + + std::string command, operand; + std::istringstream iss(line); + iss >> command >> operand; + + // process command + auto &handlers = m_Owner.GetCommandHandlers(); + auto it = handlers.find(command); + if (it != handlers.end()) { + (this->*(it->second))(operand.c_str(), operand.length()); + } else { + LogPrint(eLogError, "BOB: Unknown command ", command.c_str()); + SendReplyError("unknown command"); + } + } + } + + void BOBCommandSession::Send() { + boost::asio::async_write(m_Socket, m_SendBuffer, + boost::asio::transfer_all(), + std::bind(&BOBCommandSession::HandleSent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + + void BOBCommandSession::HandleSent(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "BOB: Command channel send error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else { + if (m_IsOpen) + Receive(); + else + Terminate(); + } + } + + void BOBCommandSession::SendReplyOK(const char *msg) { + std::ostream os(&m_SendBuffer); + os << "OK"; + if (msg) { + os << " " << msg; + } + os << std::endl; + Send(); + } + + void BOBCommandSession::SendReplyError(const char *msg) { + std::ostream os(&m_SendBuffer); + os << "ERROR " << msg << std::endl; + Send(); + } + + void BOBCommandSession::SendVersion() { + std::ostream os(&m_SendBuffer); + os << "BOB 00.00.10" << std::endl; + SendReplyOK(); + } + + void BOBCommandSession::SendRaw(const char *data) { + std::ostream os(&m_SendBuffer); + os << data << std::endl; + } + + void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out) { + // helper lambdas + const auto issetStr = [](const std::string &str) { + return str.empty() ? "not_set" : str; + }; // for inhost, outhost + const auto issetNum = [&issetStr](const int p) { + return issetStr(p == 0 ? "" : std::to_string(p)); + }; // for inport, outport + const auto destExists = [](const BOBDestination *const dest) { return dest != nullptr; }; + const auto destReady = [](const BOBDestination *const dest) { return dest->IsRunning(); }; + const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str + + // tunnel info + const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname(); + const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet(); + const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost()); + const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost()); + const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort()); + const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort()); + const bool keys = destExists(dest); // key must exist when destination is created + const bool starting = destExists(dest) && !destReady(dest); + const bool running = destExists(dest) && destReady(dest); + const bool stopping = false; + + // build line + std::stringstream ss; + ss << "DATA " + << "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " " + << "RUNNING: " << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " " + << "KEYS: " << bool_str(keys) << " " << "QUIET: " << bool_str(quiet) << " " + << "INPORT: " << inport << " " << "INHOST: " << inhost << " " + << "OUTPORT: " << outport << " " << "OUTHOST: " << outhost; + out = ss.str(); + } + + void BOBCommandSession::ZapCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: zap"); + Terminate(); + } + + void BOBCommandSession::QuitCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: quit"); + m_IsOpen = false; + SendReplyOK("Bye!"); + } + + void BOBCommandSession::StartCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: start ", m_Nickname); + if (m_IsActive) { + SendReplyError("tunnel is active"); + return; + } + if (!m_Keys.GetPublic()) // keys are set ? + { + SendReplyError("Keys must be set."); + return; + } + if (m_InPort == 0 + && m_OutHost.empty() && m_OutPort == 0) { + SendReplyError("(inhost):inport or outhost:outport must be set."); + return; + } + if (!m_InHost.empty()) { + // TODO: FIXME: temporary validation, until hostname support is added + boost::system::error_code ec; + boost::asio::ip::address::from_string(m_InHost, ec); + if (ec) { + SendReplyError("inhost must be a valid IPv4 address."); + return; + } + } + if (!m_OutHost.empty()) { + // TODO: FIXME: temporary validation, until hostname support is added + boost::system::error_code ec; + boost::asio::ip::address::from_string(m_OutHost, ec); + if (ec) { + SendReplyError("outhost must be a IPv4 address."); + return; + } + } + + if (!m_CurrentDestination) { + m_CurrentDestination = new BOBDestination(i2p::client::context.CreateNewLocalDestination(m_Keys, true, + &m_Options), // deleted in clear command + m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, + m_IsQuiet); + m_Owner.AddDestination(m_Nickname, m_CurrentDestination); + } + if (m_InPort) + m_CurrentDestination->CreateInboundTunnel(m_InPort, m_InHost); + if (m_OutPort && !m_OutHost.empty()) + m_CurrentDestination->CreateOutboundTunnel(m_OutHost, m_OutPort, m_IsQuiet); + m_CurrentDestination->Start(); + SendReplyOK("Tunnel starting"); + m_IsActive = true; + } + + void BOBCommandSession::StopCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: stop ", m_Nickname); + if (!m_IsActive) { + SendReplyError("tunnel is inactive"); + return; + } + auto dest = m_Owner.FindDestination(m_Nickname); + if (dest) { + dest->StopTunnels(); + SendReplyOK("Tunnel stopping"); + } else + SendReplyError("tunnel not found"); + m_IsActive = false; + } + + void BOBCommandSession::SetNickCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: setnick ", operand); + if (*operand) { + auto dest = m_Owner.FindDestination(operand); + if (!dest) { + m_Nickname = operand; + std::string msg("Nickname set to "); + msg += m_Nickname; + SendReplyOK(msg.c_str()); + } else + SendReplyError("tunnel is active"); + } else + SendReplyError("no nickname has been set"); + } + + void BOBCommandSession::GetNickCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: getnick ", operand); + if (*operand) { + m_CurrentDestination = m_Owner.FindDestination(operand); + if (m_CurrentDestination) { + m_Keys = m_CurrentDestination->GetKeys(); + m_IsActive = m_CurrentDestination->IsRunning(); + m_Nickname = operand; + } + if (m_Nickname == operand) { + std::string msg("Nickname set to "); + msg += m_Nickname; + SendReplyOK(msg.c_str()); + } else + SendReplyError("no nickname has been set"); + } else + SendReplyError("no nickname has been set"); + } + + void BOBCommandSession::NewkeysCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: newkeys"); + i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + if (*operand) { + try { + char *operand1 = (char *) strchr(operand, ' '); + if (operand1) { + *operand1 = 0; + operand1++; + cryptoType = std::stoi(operand1); + } + signatureType = std::stoi(operand); + } + catch (std::invalid_argument &ex) { + LogPrint(eLogWarning, "BOB: Error on newkeys: ", ex.what()); + } + } + + + m_Keys = i2p::data::PrivateKeys::CreateRandomKeys(signatureType, cryptoType); + SendReplyOK(m_Keys.GetPublic()->ToBase64().c_str()); + } + + void BOBCommandSession::SetkeysCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: setkeys ", operand); + if (*operand && m_Keys.FromBase64(operand)) + SendReplyOK(m_Keys.GetPublic()->ToBase64().c_str()); + else + SendReplyError("invalid keys"); + } + + void BOBCommandSession::GetkeysCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: getkeys"); + if (m_Keys.GetPublic()) // keys are set ? + SendReplyOK(m_Keys.ToBase64().c_str()); + else + SendReplyError("keys are not set"); + } + + void BOBCommandSession::GetdestCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: getdest"); + if (m_Keys.GetPublic()) // keys are set ? + SendReplyOK(m_Keys.GetPublic()->ToBase64().c_str()); + else + SendReplyError("keys are not set"); + } + + void BOBCommandSession::OuthostCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: outhost ", operand); + if (*operand) { + m_OutHost = operand; + SendReplyOK("outhost set"); + } else + SendReplyError("empty outhost"); + } + + void BOBCommandSession::OutportCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: outport ", operand); + if (*operand) { + m_OutPort = std::stoi(operand); + if (m_OutPort >= 0) + SendReplyOK("outbound port set"); + else + SendReplyError("port out of range"); + } else + SendReplyError("empty outport"); + } + + void BOBCommandSession::InhostCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: inhost ", operand); + if (*operand) { + m_InHost = operand; + SendReplyOK("inhost set"); + } else + SendReplyError("empty inhost"); + } + + void BOBCommandSession::InportCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: inport ", operand); + if (*operand) { + m_InPort = std::stoi(operand); + if (m_InPort >= 0) + SendReplyOK("inbound port set"); + else + SendReplyError("port out of range"); + } else + SendReplyError("empty inport"); + } + + void BOBCommandSession::QuietCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: quiet"); + if (m_Nickname.length() > 0) { + if (!m_IsActive) { + m_IsQuiet = true; + SendReplyOK("Quiet set"); + } else + SendReplyError("tunnel is active"); + } else + SendReplyError("no nickname has been set"); + } + + void BOBCommandSession::LookupCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: lookup ", operand); + if (*operand) { + auto addr = context.GetAddressBook().GetAddress(operand); + if (!addr) { + SendReplyError("Address Not found"); + return; + } + auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination() + : i2p::client::context.GetSharedLocalDestination(); + if (addr->IsIdentHash()) { + // we might have leaseset already + auto leaseSet = localDestination->FindLeaseSet(addr->identHash); + if (leaseSet) { + SendReplyOK(leaseSet->GetIdentity()->ToBase64().c_str()); + return; + } + } + // trying to request + auto s = shared_from_this(); + auto requstCallback = [s](std::shared_ptr ls) { + if (ls) + s->SendReplyOK(ls->GetIdentity()->ToBase64().c_str()); + else + s->SendReplyError("LeaseSet Not found"); + }; + if (addr->IsIdentHash()) + localDestination->RequestDestination(addr->identHash, requstCallback); + else + localDestination->RequestDestinationWithEncryptedLeaseSet(addr->blindedPublicKey, requstCallback); + } else + SendReplyError("empty lookup address"); + } + + void BOBCommandSession::LookupLocalCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: lookup local ", operand); + if (*operand) { + auto addr = context.GetAddressBook().GetAddress(operand); + if (!addr) { + SendReplyError("Address Not found"); + return; + } + auto ls = i2p::data::netdb.FindLeaseSet(addr->identHash); + if (ls) + SendReplyOK(ls->GetIdentity()->ToBase64().c_str()); + else + SendReplyError("Local LeaseSet Not found"); + } else + SendReplyError("empty lookup address"); + } + + void BOBCommandSession::ClearCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: clear"); + m_Owner.DeleteDestination(m_Nickname); + m_Nickname = ""; + SendReplyOK("cleared"); + } + + void BOBCommandSession::ListCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: list"); + std::string statusLine; + bool sentCurrent = false; + const auto &destinations = m_Owner.GetDestinations(); + for (const auto &it: destinations) { + BuildStatusLine(false, it.second, statusLine); + SendRaw(statusLine.c_str()); + if (m_Nickname.compare(it.second->GetNickname()) == 0) + sentCurrent = true; + } + if (!sentCurrent && !m_Nickname.empty()) { + // add the current tunnel to the list. + // this is for the incomplete tunnel which has not been started yet. + BuildStatusLine(true, m_CurrentDestination, statusLine); + SendRaw(statusLine.c_str()); + } + SendReplyOK("Listing done"); + } + + void BOBCommandSession::OptionCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: option ", operand); + const char *value = strchr(operand, '='); + if (value) { + std::string msg("option "); + *(const_cast(value)) = 0; + m_Options[operand] = value + 1; + msg += operand; + *(const_cast(value)) = '='; + msg += " set to "; + msg += value + 1; + SendReplyOK(msg.c_str()); + } else + SendReplyError("malformed"); + } + + void BOBCommandSession::StatusCommandHandler(const char *operand, size_t len) { + LogPrint(eLogDebug, "BOB: status ", operand); + const std::string name = operand; + std::string statusLine; + + // always prefer destination + auto dest = m_Owner.FindDestination(name); + if (dest) { + // tunnel destination exists + BuildStatusLine(false, dest, statusLine); + SendReplyOK(statusLine.c_str()); + } else { + if (m_Nickname == name && !name.empty()) { + // tunnel is incomplete / has not been started yet + BuildStatusLine(true, nullptr, statusLine); + SendReplyOK(statusLine.c_str()); + } else { + SendReplyError("no nickname has been set"); + } + } + } + + void BOBCommandSession::HelpCommandHandler(const char *operand, size_t len) { + auto helpStrings = m_Owner.GetHelpStrings(); + if (!*operand) { + std::stringstream ss; + ss << "COMMANDS:"; + for (auto const &x: helpStrings) { + ss << " " << x.first; + } + const std::string &str = ss.str(); + SendReplyOK(str.c_str()); + } else { + auto it = helpStrings.find(operand); + if (it != helpStrings.end()) { + SendReplyOK(it->second.c_str()); + return; + } + SendReplyError("No such command"); + } + } + + BOBCommandChannel::BOBCommandChannel(const std::string &address, int port) : + RunnableService("BOB"), + m_Acceptor(GetIOService(), + boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) { + // command -> handler + m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; + m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler; + m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler; + m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler; + m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler; + m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler; + m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler; + m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler; + m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler; + m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; + m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; + m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; + m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler; + m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; + m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; + m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; + m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler; + m_CommandHandlers[BOB_COMMAND_HELP] = &BOBCommandSession::HelpCommandHandler; + // command -> help string + m_HelpStrings[BOB_COMMAND_ZAP] = BOB_HELP_ZAP; + m_HelpStrings[BOB_COMMAND_QUIT] = BOB_HELP_QUIT; + m_HelpStrings[BOB_COMMAND_START] = BOB_HELP_START; + m_HelpStrings[BOB_COMMAND_STOP] = BOB_HELP_STOP; + m_HelpStrings[BOB_COMMAND_SETNICK] = BOB_HELP_SETNICK; + m_HelpStrings[BOB_COMMAND_GETNICK] = BOB_HELP_GETNICK; + m_HelpStrings[BOB_COMMAND_NEWKEYS] = BOB_HELP_NEWKEYS; + m_HelpStrings[BOB_COMMAND_GETKEYS] = BOB_HELP_GETKEYS; + m_HelpStrings[BOB_COMMAND_SETKEYS] = BOB_HELP_SETKEYS; + m_HelpStrings[BOB_COMMAND_GETDEST] = BOB_HELP_GETDEST; + m_HelpStrings[BOB_COMMAND_OUTHOST] = BOB_HELP_OUTHOST; + m_HelpStrings[BOB_COMMAND_OUTPORT] = BOB_HELP_OUTPORT; + m_HelpStrings[BOB_COMMAND_INHOST] = BOB_HELP_INHOST; + m_HelpStrings[BOB_COMMAND_INPORT] = BOB_HELP_INPORT; + m_HelpStrings[BOB_COMMAND_QUIET] = BOB_HELP_QUIET; + m_HelpStrings[BOB_COMMAND_LOOKUP] = BOB_HELP_LOOKUP; + m_HelpStrings[BOB_COMMAND_CLEAR] = BOB_HELP_CLEAR; + m_HelpStrings[BOB_COMMAND_LIST] = BOB_HELP_LIST; + m_HelpStrings[BOB_COMMAND_OPTION] = BOB_HELP_OPTION; + m_HelpStrings[BOB_COMMAND_STATUS] = BOB_HELP_STATUS; + m_HelpStrings[BOB_COMMAND_HELP] = BOB_HELP_HELP; + } + + BOBCommandChannel::~BOBCommandChannel() { + if (IsRunning()) + Stop(); + for (const auto &it: m_Destinations) + delete it.second; + } + + void BOBCommandChannel::Start() { + Accept(); + StartIOService(); + } + + void BOBCommandChannel::Stop() { + for (auto &it: m_Destinations) + it.second->Stop(); + m_Acceptor.cancel(); + StopIOService(); + } + + void BOBCommandChannel::AddDestination(const std::string &name, BOBDestination *dest) { + m_Destinations[name] = dest; + } + + void BOBCommandChannel::DeleteDestination(const std::string &name) { + auto it = m_Destinations.find(name); + if (it != m_Destinations.end()) { + it->second->Stop(); + delete it->second; + m_Destinations.erase(it); + } + } + + BOBDestination *BOBCommandChannel::FindDestination(const std::string &name) { + auto it = m_Destinations.find(name); + if (it != m_Destinations.end()) + return it->second; + return nullptr; + } + + void BOBCommandChannel::Accept() { + auto newSession = std::make_shared(*this); + m_Acceptor.async_accept(newSession->GetSocket(), std::bind(&BOBCommandChannel::HandleAccept, this, + std::placeholders::_1, newSession)); + } + + void BOBCommandChannel::HandleAccept(const boost::system::error_code &ecode, + std::shared_ptr session) { + if (ecode != boost::asio::error::operation_aborted) + Accept(); + + if (!ecode) { + LogPrint(eLogInfo, "BOB: New command connection from ", session->GetSocket().remote_endpoint()); + session->SendVersion(); + } else + LogPrint(eLogError, "BOB: Accept error: ", ecode.message()); + } + } } diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h index 2e6314e2..90e341e0 100644 --- a/libi2pd_client/BOB.h +++ b/libi2pd_client/BOB.h @@ -21,263 +21,326 @@ #include "Identity.h" #include "LeaseSet.h" -namespace i2p -{ -namespace client -{ - const size_t BOB_COMMAND_BUFFER_SIZE = 1024; - const char BOB_COMMAND_ZAP[] = "zap"; - const char BOB_COMMAND_QUIT[] = "quit"; - const char BOB_COMMAND_START[] = "start"; - const char BOB_COMMAND_STOP[] = "stop"; - const char BOB_COMMAND_SETNICK[] = "setnick"; - const char BOB_COMMAND_GETNICK[] = "getnick"; - const char BOB_COMMAND_NEWKEYS[] = "newkeys"; - const char BOB_COMMAND_GETKEYS[] = "getkeys"; - const char BOB_COMMAND_SETKEYS[] = "setkeys"; - const char BOB_COMMAND_GETDEST[] = "getdest"; - const char BOB_COMMAND_OUTHOST[] = "outhost"; - const char BOB_COMMAND_OUTPORT[] = "outport"; - const char BOB_COMMAND_INHOST[] = "inhost"; - const char BOB_COMMAND_INPORT[] = "inport"; - const char BOB_COMMAND_QUIET[] = "quiet"; - const char BOB_COMMAND_LOOKUP[] = "lookup"; - const char BOB_COMMAND_LOOKUP_LOCAL[] = "lookuplocal"; - const char BOB_COMMAND_CLEAR[] = "clear"; - const char BOB_COMMAND_LIST[] = "list"; - const char BOB_COMMAND_OPTION[] = "option"; - const char BOB_COMMAND_STATUS[] = "status"; - const char BOB_COMMAND_HELP[] = "help"; +namespace i2p { + namespace client { + const size_t BOB_COMMAND_BUFFER_SIZE = 1024; + const char BOB_COMMAND_ZAP[] = "zap"; + const char BOB_COMMAND_QUIT[] = "quit"; + const char BOB_COMMAND_START[] = "start"; + const char BOB_COMMAND_STOP[] = "stop"; + const char BOB_COMMAND_SETNICK[] = "setnick"; + const char BOB_COMMAND_GETNICK[] = "getnick"; + const char BOB_COMMAND_NEWKEYS[] = "newkeys"; + const char BOB_COMMAND_GETKEYS[] = "getkeys"; + const char BOB_COMMAND_SETKEYS[] = "setkeys"; + const char BOB_COMMAND_GETDEST[] = "getdest"; + const char BOB_COMMAND_OUTHOST[] = "outhost"; + const char BOB_COMMAND_OUTPORT[] = "outport"; + const char BOB_COMMAND_INHOST[] = "inhost"; + const char BOB_COMMAND_INPORT[] = "inport"; + const char BOB_COMMAND_QUIET[] = "quiet"; + const char BOB_COMMAND_LOOKUP[] = "lookup"; + const char BOB_COMMAND_LOOKUP_LOCAL[] = "lookuplocal"; + const char BOB_COMMAND_CLEAR[] = "clear"; + const char BOB_COMMAND_LIST[] = "list"; + const char BOB_COMMAND_OPTION[] = "option"; + const char BOB_COMMAND_STATUS[] = "status"; + const char BOB_COMMAND_HELP[] = "help"; - const char BOB_HELP_ZAP[] = "zap - Shuts down BOB."; - const char BOB_HELP_QUIT[] = "quit - Quits this session with BOB."; - const char BOB_HELP_START[] = "start - Starts the current nicknamed tunnel."; - const char BOB_HELP_STOP[] = "stop - Stops the current nicknamed tunnel."; - const char BOB_HELP_SETNICK[] = "setnick - Creates a new nickname."; - const char BOB_HELP_GETNICK[] = "getnick - Sets the nickname from the database."; - const char BOB_HELP_NEWKEYS[] = "newkeys - Generate a new keypair for the current nickname."; - const char BOB_HELP_GETKEYS[] = "getkeys - Return the keypair for the current nickname."; - const char BOB_HELP_SETKEYS[] = "setkeys - Sets the keypair for the current nickname."; - const char BOB_HELP_GETDEST[] = "getdest - Return the destination for the current nickname."; - const char BOB_HELP_OUTHOST[] = "outhost - Set the outhound hostname or IP."; - const char BOB_HELP_OUTPORT[] = "outport - Set the outbound port that nickname contacts."; - const char BOB_HELP_INHOST[] = "inhost - Set the inbound hostname or IP."; - const char BOB_HELP_INPORT[] = "inport - Set the inbound port number nickname listens on."; - const char BOB_HELP_QUIET[] = "quiet - Whether to send the incoming destination."; - const char BOB_HELP_LOOKUP[] = "lookup - Look up an I2P hostname."; - const char BOB_HELP_CLEAR[] = "clear - Clear the current nickname out of the list."; - const char BOB_HELP_LIST[] = "list - List all tunnels."; - const char BOB_HELP_OPTION[] = "option = - Set an option. NOTE: Don't use any spaces."; - const char BOB_HELP_STATUS[] = "status - Display status of a nicknamed tunnel."; - const char BOB_HELP_HELP [] = "help - Get help on a command."; + const char BOB_HELP_ZAP[] = "zap - Shuts down BOB."; + const char BOB_HELP_QUIT[] = "quit - Quits this session with BOB."; + const char BOB_HELP_START[] = "start - Starts the current nicknamed tunnel."; + const char BOB_HELP_STOP[] = "stop - Stops the current nicknamed tunnel."; + const char BOB_HELP_SETNICK[] = "setnick - Creates a new nickname."; + const char BOB_HELP_GETNICK[] = "getnick - Sets the nickname from the database."; + const char BOB_HELP_NEWKEYS[] = "newkeys - Generate a new keypair for the current nickname."; + const char BOB_HELP_GETKEYS[] = "getkeys - Return the keypair for the current nickname."; + const char BOB_HELP_SETKEYS[] = "setkeys - Sets the keypair for the current nickname."; + const char BOB_HELP_GETDEST[] = "getdest - Return the destination for the current nickname."; + const char BOB_HELP_OUTHOST[] = "outhost - Set the outhound hostname or IP."; + const char BOB_HELP_OUTPORT[] = "outport - Set the outbound port that nickname contacts."; + const char BOB_HELP_INHOST[] = "inhost - Set the inbound hostname or IP."; + const char BOB_HELP_INPORT[] = "inport - Set the inbound port number nickname listens on."; + const char BOB_HELP_QUIET[] = "quiet - Whether to send the incoming destination."; + const char BOB_HELP_LOOKUP[] = "lookup - Look up an I2P hostname."; + const char BOB_HELP_CLEAR[] = "clear - Clear the current nickname out of the list."; + const char BOB_HELP_LIST[] = "list - List all tunnels."; + const char BOB_HELP_OPTION[] = "option = - Set an option. NOTE: Don't use any spaces."; + const char BOB_HELP_STATUS[] = "status - Display status of a nicknamed tunnel."; + const char BOB_HELP_HELP[] = "help - Get help on a command."; - class BOBI2PTunnel: public I2PService - { - public: + class BOBI2PTunnel : public I2PService { + public: - BOBI2PTunnel (std::shared_ptr localDestination): - I2PService (localDestination) {}; + BOBI2PTunnel(std::shared_ptr localDestination) : + I2PService(localDestination) {}; - virtual void Start () {}; - virtual void Stop () {}; - }; + virtual void Start() {}; - class BOBI2PInboundTunnel: public BOBI2PTunnel - { - struct AddressReceiver - { - std::shared_ptr socket; - char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address - uint8_t * data; // pointer to buffer - size_t dataLen, bufferOffset; + virtual void Stop() {}; + }; - AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; - }; + class BOBI2PInboundTunnel : public BOBI2PTunnel { + struct AddressReceiver { + std::shared_ptr socket; + char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address + uint8_t *data; // pointer to buffer + size_t dataLen, bufferOffset; - public: + AddressReceiver() : data(nullptr), dataLen(0), bufferOffset(0) {}; + }; - BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr localDestination); - ~BOBI2PInboundTunnel (); + public: - void Start (); - void Stop (); + BOBI2PInboundTunnel(const boost::asio::ip::tcp::endpoint &ep, + std::shared_ptr localDestination); - private: + ~BOBI2PInboundTunnel(); - void Accept (); - void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver); + void Start(); - void ReceiveAddress (std::shared_ptr receiver); - void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr receiver); + void Stop(); - void HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver); + private: - void CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet); + void Accept(); - private: + void HandleAccept(const boost::system::error_code &ecode, std::shared_ptr receiver); - boost::asio::ip::tcp::acceptor m_Acceptor; - }; + void ReceiveAddress(std::shared_ptr receiver); - class BOBI2POutboundTunnel: public BOBI2PTunnel - { - public: + void HandleReceivedAddress(const boost::system::error_code &ecode, std::size_t bytes_transferred, + std::shared_ptr receiver); - BOBI2POutboundTunnel (const std::string& outhost, int port, std::shared_ptr localDestination, bool quiet); + void HandleDestinationRequestComplete(std::shared_ptr leaseSet, + std::shared_ptr receiver); - void Start (); - void Stop (); + void CreateConnection(std::shared_ptr receiver, + std::shared_ptr leaseSet); - void SetQuiet () { m_IsQuiet = true; }; + private: - private: + boost::asio::ip::tcp::acceptor m_Acceptor; + }; - void Accept (); - void HandleAccept (std::shared_ptr stream); + class BOBI2POutboundTunnel : public BOBI2PTunnel { + public: - private: + BOBI2POutboundTunnel(const std::string &outhost, int port, + std::shared_ptr localDestination, bool quiet); - boost::asio::ip::tcp::endpoint m_Endpoint; - bool m_IsQuiet; - }; + void Start(); + + void Stop(); + + void SetQuiet() { m_IsQuiet = true; }; + + private: + + void Accept(); + + void HandleAccept(std::shared_ptr stream); + + private: + + boost::asio::ip::tcp::endpoint m_Endpoint; + bool m_IsQuiet; + }; - class BOBDestination - { - public: + class BOBDestination { + public: - BOBDestination (std::shared_ptr localDestination, - const std::string &nickname, const std::string &inhost, const std::string &outhost, - const int inport, const int outport, const bool quiet); - ~BOBDestination (); + BOBDestination(std::shared_ptr localDestination, + const std::string &nickname, const std::string &inhost, const std::string &outhost, + const int inport, const int outport, const bool quiet); - void Start (); - void Stop (); - void StopTunnels (); - void CreateInboundTunnel (int port, const std::string& inhost); - void CreateOutboundTunnel (const std::string& outhost, int port, bool quiet); - const std::string& GetNickname() const { return m_Nickname; } - const std::string& GetInHost() const { return m_InHost; } - const std::string& GetOutHost() const { return m_OutHost; } - int GetInPort() const { return m_InPort; } - int GetOutPort() const { return m_OutPort; } - bool GetQuiet() const { return m_Quiet; } - bool IsRunning() const { return m_IsRunning; } - const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; - std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; + ~BOBDestination(); - private: + void Start(); - std::shared_ptr m_LocalDestination; - BOBI2POutboundTunnel * m_OutboundTunnel; - BOBI2PInboundTunnel * m_InboundTunnel; + void Stop(); - std::string m_Nickname; - std::string m_InHost, m_OutHost; - int m_InPort, m_OutPort; - bool m_Quiet; - bool m_IsRunning; - }; + void StopTunnels(); - class BOBCommandChannel; - class BOBCommandSession: public std::enable_shared_from_this - { - public: + void CreateInboundTunnel(int port, const std::string &inhost); - BOBCommandSession (BOBCommandChannel& owner); - ~BOBCommandSession (); - void Terminate (); + void CreateOutboundTunnel(const std::string &outhost, int port, bool quiet); - boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; - void SendVersion (); + const std::string &GetNickname() const { return m_Nickname; } - // command handlers - void ZapCommandHandler (const char * operand, size_t len); - void QuitCommandHandler (const char * operand, size_t len); - void StartCommandHandler (const char * operand, size_t len); - void StopCommandHandler (const char * operand, size_t len); - void SetNickCommandHandler (const char * operand, size_t len); - void GetNickCommandHandler (const char * operand, size_t len); - void NewkeysCommandHandler (const char * operand, size_t len); - void SetkeysCommandHandler (const char * operand, size_t len); - void GetkeysCommandHandler (const char * operand, size_t len); - void GetdestCommandHandler (const char * operand, size_t len); - void OuthostCommandHandler (const char * operand, size_t len); - void OutportCommandHandler (const char * operand, size_t len); - void InhostCommandHandler (const char * operand, size_t len); - void InportCommandHandler (const char * operand, size_t len); - void QuietCommandHandler (const char * operand, size_t len); - void LookupCommandHandler (const char * operand, size_t len); - void LookupLocalCommandHandler (const char * operand, size_t len); - void ClearCommandHandler (const char * operand, size_t len); - void ListCommandHandler (const char * operand, size_t len); - void OptionCommandHandler (const char * operand, size_t len); - void StatusCommandHandler (const char * operand, size_t len); - void HelpCommandHandler (const char * operand, size_t len); + const std::string &GetInHost() const { return m_InHost; } - private: + const std::string &GetOutHost() const { return m_OutHost; } - void Receive (); - void HandleReceivedLine(const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + int GetInPort() const { return m_InPort; } - void Send (); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendReplyOK (const char * msg = nullptr); - void SendReplyError (const char * msg); - void SendRaw (const char * data); + int GetOutPort() const { return m_OutPort; } - void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out); + bool GetQuiet() const { return m_Quiet; } - private: + bool IsRunning() const { return m_IsRunning; } - BOBCommandChannel& m_Owner; - boost::asio::ip::tcp::socket m_Socket; - boost::asio::streambuf m_ReceiveBuffer, m_SendBuffer; - bool m_IsOpen, m_IsQuiet, m_IsActive; - std::string m_Nickname, m_InHost, m_OutHost; - int m_InPort, m_OutPort; - i2p::data::PrivateKeys m_Keys; - std::map m_Options; - BOBDestination * m_CurrentDestination; - }; - typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); + const i2p::data::PrivateKeys &GetKeys() const { return m_LocalDestination->GetPrivateKeys(); }; - class BOBCommandChannel: private i2p::util::RunnableService - { - public: + std::shared_ptr GetLocalDestination() const { return m_LocalDestination; }; - BOBCommandChannel (const std::string& address, int port); - ~BOBCommandChannel (); + private: - void Start (); - void Stop (); + std::shared_ptr m_LocalDestination; + BOBI2POutboundTunnel *m_OutboundTunnel; + BOBI2PInboundTunnel *m_InboundTunnel; - boost::asio::io_service& GetService () { return GetIOService (); }; - void AddDestination (const std::string& name, BOBDestination * dest); - void DeleteDestination (const std::string& name); - BOBDestination * FindDestination (const std::string& name); + std::string m_Nickname; + std::string m_InHost, m_OutHost; + int m_InPort, m_OutPort; + bool m_Quiet; + bool m_IsRunning; + }; - private: + class BOBCommandChannel; - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); + class BOBCommandSession : public std::enable_shared_from_this { + public: - private: + BOBCommandSession(BOBCommandChannel &owner); - boost::asio::ip::tcp::acceptor m_Acceptor; - std::map m_Destinations; - std::map m_CommandHandlers; - std::map m_HelpStrings; + ~BOBCommandSession(); - public: + void Terminate(); - const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; }; - const decltype(m_HelpStrings)& GetHelpStrings () const { return m_HelpStrings; }; - const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; - }; -} + boost::asio::ip::tcp::socket &GetSocket() { return m_Socket; }; + + void SendVersion(); + + // command handlers + void ZapCommandHandler(const char *operand, size_t len); + + void QuitCommandHandler(const char *operand, size_t len); + + void StartCommandHandler(const char *operand, size_t len); + + void StopCommandHandler(const char *operand, size_t len); + + void SetNickCommandHandler(const char *operand, size_t len); + + void GetNickCommandHandler(const char *operand, size_t len); + + void NewkeysCommandHandler(const char *operand, size_t len); + + void SetkeysCommandHandler(const char *operand, size_t len); + + void GetkeysCommandHandler(const char *operand, size_t len); + + void GetdestCommandHandler(const char *operand, size_t len); + + void OuthostCommandHandler(const char *operand, size_t len); + + void OutportCommandHandler(const char *operand, size_t len); + + void InhostCommandHandler(const char *operand, size_t len); + + void InportCommandHandler(const char *operand, size_t len); + + void QuietCommandHandler(const char *operand, size_t len); + + void LookupCommandHandler(const char *operand, size_t len); + + void LookupLocalCommandHandler(const char *operand, size_t len); + + void ClearCommandHandler(const char *operand, size_t len); + + void ListCommandHandler(const char *operand, size_t len); + + void OptionCommandHandler(const char *operand, size_t len); + + void StatusCommandHandler(const char *operand, size_t len); + + void HelpCommandHandler(const char *operand, size_t len); + + private: + + void Receive(); + + void HandleReceivedLine(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void Send(); + + void HandleSent(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void SendReplyOK(const char *msg = nullptr); + + void SendReplyError(const char *msg); + + void SendRaw(const char *data); + + void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out); + + private: + + BOBCommandChannel &m_Owner; + boost::asio::ip::tcp::socket m_Socket; + boost::asio::streambuf m_ReceiveBuffer, m_SendBuffer; + bool m_IsOpen, m_IsQuiet, m_IsActive; + std::string m_Nickname, m_InHost, m_OutHost; + int m_InPort, m_OutPort; + i2p::data::PrivateKeys m_Keys; + std::map m_Options; + BOBDestination *m_CurrentDestination; + }; + + typedef void (BOBCommandSession::*BOBCommandHandler)(const char *operand, size_t len); + + class BOBCommandChannel : private i2p::util::RunnableService { + public: + + BOBCommandChannel(const std::string &address, int port); + + ~BOBCommandChannel(); + + void Start(); + + void Stop(); + + boost::asio::io_service &GetService() { return GetIOService(); }; + + void AddDestination(const std::string &name, BOBDestination *dest); + + void DeleteDestination(const std::string &name); + + BOBDestination *FindDestination(const std::string &name); + + private: + + void Accept(); + + void HandleAccept(const boost::system::error_code &ecode, std::shared_ptr session); + + private: + + boost::asio::ip::tcp::acceptor m_Acceptor; + std::map m_Destinations; + std::map m_CommandHandlers; + std::map m_HelpStrings; + + public: + + const decltype(m_CommandHandlers) + & + + GetCommandHandlers() const { return m_CommandHandlers; }; + const decltype(m_HelpStrings) + & + + GetHelpStrings() const { return m_HelpStrings; }; + const decltype(m_Destinations) + & + + GetDestinations() const { return m_Destinations; }; + }; + } } #endif diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 3f670f06..3d9f869b 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -19,995 +19,955 @@ #include "SOCKS.h" #include "MatchedDestination.h" -namespace i2p -{ -namespace client -{ - ClientContext context; - - ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), - m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), - m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr) - { - } - - ClientContext::~ClientContext () - { - delete m_HttpProxy; - delete m_SocksProxy; - delete m_SamBridge; - delete m_BOBCommandChannel; - delete m_I2CPServer; - } - - void ClientContext::Start () - { - // shared local destination - if (!m_SharedLocalDestination) - CreateNewSharedLocalDestination (); - - // addressbook - m_AddressBook.Start (); - - // HTTP proxy - ReadHttpProxy (); - - // SOCKS proxy - ReadSocksProxy (); - - // I2P tunnels - ReadTunnels (); - - // SAM - bool sam; i2p::config::GetOption("sam.enabled", sam); - if (sam) - { - std::string samAddr; i2p::config::GetOption("sam.address", samAddr); - uint16_t samPort; i2p::config::GetOption("sam.port", samPort); - bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); - LogPrint(eLogInfo, "Clients: Starting SAM bridge at ", samAddr, ":", samPort); - try - { - m_SamBridge = new SAMBridge (samAddr, samPort, singleThread); - m_SamBridge->Start (); - } - catch (std::exception& e) - { - LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); - ThrowFatal ("Unable to start SAM bridge at ", samAddr, ":", samPort, ": ", e.what ()); - } - } - - // BOB - bool bob; i2p::config::GetOption("bob.enabled", bob); - if (bob) { - std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr); - uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort); - LogPrint(eLogInfo, "Clients: Starting BOB command channel at ", bobAddr, ":", bobPort); - try - { - m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort); - m_BOBCommandChannel->Start (); - } - catch (std::exception& e) - { - LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what()); - ThrowFatal ("Unable to start BOB bridge at ", bobAddr, ":", bobPort, ": ", e.what ()); - } - } - - // I2CP - bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp); - if (i2cp) - { - std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr); - uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort); - bool singleThread; i2p::config::GetOption("i2cp.singlethread", singleThread); - LogPrint(eLogInfo, "Clients: Starting I2CP at ", i2cpAddr, ":", i2cpPort); - try - { - m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort, singleThread); - m_I2CPServer->Start (); - } - catch (std::exception& e) - { - LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what()); - ThrowFatal ("Unable to start I2CP at ", i2cpAddr, ":", i2cpPort, ": ", e.what ()); - } - } - - m_AddressBook.StartResolvers (); - - // start UDP cleanup - if (!m_ServerForwards.empty ()) - { - m_CleanupUDPTimer.reset (new boost::asio::deadline_timer(m_SharedLocalDestination->GetService ())); - ScheduleCleanupUDP(); - } - } - - void ClientContext::Stop () - { - if (m_HttpProxy) - { - LogPrint(eLogInfo, "Clients: Stopping HTTP Proxy"); - m_HttpProxy->Stop(); - delete m_HttpProxy; - m_HttpProxy = nullptr; - } - - if (m_SocksProxy) - { - LogPrint(eLogInfo, "Clients: Stopping SOCKS Proxy"); - m_SocksProxy->Stop(); - delete m_SocksProxy; - m_SocksProxy = nullptr; - } - - for (auto& it: m_ClientTunnels) - { - LogPrint(eLogInfo, "Clients: Stopping I2P client tunnel on port ", it.first); - it.second->Stop (); - } - m_ClientTunnels.clear (); - - for (auto& it: m_ServerTunnels) - { - LogPrint(eLogInfo, "Clients: Stopping I2P server tunnel"); - it.second->Stop (); - } - m_ServerTunnels.clear (); - - if (m_SamBridge) - { - LogPrint(eLogInfo, "Clients: Stopping SAM bridge"); - m_SamBridge->Stop (); - delete m_SamBridge; - m_SamBridge = nullptr; - } - - if (m_BOBCommandChannel) - { - LogPrint(eLogInfo, "Clients: Stopping BOB command channel"); - m_BOBCommandChannel->Stop (); - delete m_BOBCommandChannel; - m_BOBCommandChannel = nullptr; - } - - if (m_I2CPServer) - { - LogPrint(eLogInfo, "Clients: Stopping I2CP"); - m_I2CPServer->Stop (); - delete m_I2CPServer; - m_I2CPServer = nullptr; - } - - LogPrint(eLogInfo, "Clients: Stopping AddressBook"); - m_AddressBook.Stop (); - - { - std::lock_guard lock(m_ForwardsMutex); - m_ServerForwards.clear(); - m_ClientForwards.clear(); - } - - if (m_CleanupUDPTimer) - { - m_CleanupUDPTimer->cancel (); - m_CleanupUDPTimer = nullptr; - } - - for (auto& it: m_Destinations) - it.second->Stop (); - m_Destinations.clear (); - - m_SharedLocalDestination->Release (); - m_SharedLocalDestination = nullptr; - } - - void ClientContext::ReloadConfig () - { - // TODO: handle config changes - /*std::string config; i2p::config::GetOption("conf", config); - i2p::config::ParseConfig(config);*/ - - // change shared local destination - m_SharedLocalDestination->Release (); - CreateNewSharedLocalDestination (); - - // recreate HTTP proxy - if (m_HttpProxy) - { - m_HttpProxy->Stop (); - delete m_HttpProxy; - m_HttpProxy = nullptr; - } - ReadHttpProxy (); - - // recreate SOCKS proxy - if (m_SocksProxy) - { - m_SocksProxy->Stop (); - delete m_SocksProxy; - m_SocksProxy = nullptr; - } - ReadSocksProxy (); - - // handle tunnels - // reset isUpdated for each tunnel - VisitTunnels (false); - // reload tunnels - ReadTunnels(); - // delete not updated tunnels (not in config anymore) - VisitTunnels (true); - - // delete unused destinations - std::unique_lock l(m_DestinationsMutex); - for (auto it = m_Destinations.begin (); it != m_Destinations.end ();) - { - auto dest = it->second; - if (dest->GetRefCounter () > 0) ++it; // skip - else - { - dest->Stop (); - it = m_Destinations.erase (it); - } - } - } - - bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, - i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType) - { - static const std::string transient("transient"); - if (!filename.compare (0, transient.length (), transient)) // starts with transient - { - keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); - LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); - return true; - } - - bool success = true; - std::string fullPath = i2p::fs::DataDirPath (filename); - std::ifstream s(fullPath, std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0, std::ios::end); - size_t len = s.tellg(); - s.seekg (0, std::ios::beg); - uint8_t * buf = new uint8_t[len]; - s.read ((char *)buf, len); - if(!keys.FromBuffer (buf, len)) - { - LogPrint (eLogError, "Clients: Failed to load keyfile ", filename); - success = false; - } - else - LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded"); - delete[] buf; - } - else - { - LogPrint (eLogError, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); - keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - size_t len = keys.GetFullLen (); - uint8_t * buf = new uint8_t[len]; - len = keys.ToBuffer (buf, len); - f.write ((char *)buf, len); - delete[] buf; - - LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); - } - return success; - } - - std::vector > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination) - { - std::vector > infos; - std::lock_guard lock(m_ForwardsMutex); - for(const auto & c : m_ClientForwards) - { - if (c.second->IsLocalDestination(destination)) - { - for (auto & i : c.second->GetSessions()) infos.push_back(i); - break; - } - } - for(const auto & s : m_ServerForwards) - { - if(std::get<0>(s.first) == destination) - { - for( auto & i : s.second->GetSessions()) infos.push_back(i); - break; - } - } - return infos; - } - - std::shared_ptr ClientContext::CreateNewLocalDestination (bool isPublic, - i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); - auto localDestination = std::make_shared (keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - std::shared_ptr ClientContext::CreateNewLocalDestination ( - boost::asio::io_service& service, bool isPublic, - i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); - auto localDestination = std::make_shared (service, keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - std::shared_ptr ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params) - { - auto localDestination = std::make_shared(keys, name, params); - AddLocalDestination (localDestination); - return localDestination; - } - - void ClientContext::AddLocalDestination (std::shared_ptr localDestination) - { - std::unique_lock l(m_DestinationsMutex); - m_Destinations[localDestination->GetIdentHash ()] = localDestination; - localDestination->Start (); - } - - void ClientContext::DeleteLocalDestination (std::shared_ptr destination) - { - if (!destination) return; - auto it = m_Destinations.find (destination->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - auto d = it->second; - { - std::unique_lock l(m_DestinationsMutex); - m_Destinations.erase (it); - } - d->Stop (); - } - } - - std::shared_ptr ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params) - { - auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); - it->second->Start (); // make sure to start - return it->second; - } - auto localDestination = std::make_shared (keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - std::shared_ptr ClientContext::CreateNewLocalDestination (boost::asio::io_service& service, - const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) - { - auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); - it->second->Start (); // make sure to start - return it->second; - } - auto localDestination = std::make_shared (service, keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - void ClientContext::CreateNewSharedLocalDestination () - { - std::map params - { - { I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "3" }, - { I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "3" }, - { I2CP_PARAM_LEASESET_TYPE, "3" }, - { I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4" } - }; - m_SharedLocalDestination = CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, ¶ms); // non-public, EDDSA - m_SharedLocalDestination->Acquire (); - } - - std::shared_ptr ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const - { - auto it = m_Destinations.find (destination); - if (it != m_Destinations.end ()) - return it->second; - return nullptr; - } - - template - std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const - { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); - } - - template - std::string ClientContext::GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const - { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value); - } - - template - void ClientContext::ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map& options) const - { - for (auto it: section.second) - { - if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group)) - options[it.first] = it.second.get_value (""); - } - } - - template - void ClientContext::ReadI2CPOptions (const Section& section, bool isServer, std::map& options) const - { - options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH); - options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH); - options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY); - options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY); - options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE); - options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE); - options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); - options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); - options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); - options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); - options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); - options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); - std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4"); - if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; - std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); - if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; - auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0); - if (authType != "0") // auth is set - { - options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType; - if (authType == "1") // DH - ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_DH, options); - else if (authType == "2") // PSK - ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options); - } - std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, ""); - if (explicitPeers.length () > 0) options[I2CP_PARAM_EXPLICIT_PEERS] = explicitPeers; - std::string ratchetInboundTags = GetI2CPStringOption(section, I2CP_PARAM_RATCHET_INBOUND_TAGS, ""); - if (ratchetInboundTags.length () > 0) options[I2CP_PARAM_RATCHET_INBOUND_TAGS] = ratchetInboundTags; - } - - void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const - { - std::string value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value)) - options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value)) - options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, value)) - options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) - options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) - options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, value)) - options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value)) - options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) - options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_TYPE, value)) - options[I2CP_PARAM_LEASESET_TYPE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value)) - options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ()) - options[I2CP_PARAM_LEASESET_PRIV_KEY] = value; - } - - void ClientContext::ReadTunnels () - { - int numClientTunnels = 0, numServerTunnels = 0; - std::string tunConf; i2p::config::GetOption("tunconf", tunConf); - if (tunConf.empty ()) - tunConf = i2p::fs::DataDirPath ("tunnels.conf"); - - LogPrint(eLogDebug, "Clients: Tunnels config file: ", tunConf); - ReadTunnels (tunConf, numClientTunnels, numServerTunnels); - - std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir); - if (tunDir.empty ()) - tunDir = i2p::fs::DataDirPath ("tunnels.d"); - - if (i2p::fs::Exists (tunDir)) - { - std::vector files; - if (i2p::fs::ReadDir (tunDir, files)) - { - for (auto& it: files) - { - if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf" - LogPrint(eLogDebug, "Clients: Tunnels extra config file: ", it); - ReadTunnels (it, numClientTunnels, numServerTunnels); - } - } - } - - LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created"); - LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); - } - - void ClientContext::ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels) - { - boost::property_tree::ptree pt; - try { - boost::property_tree::read_ini (tunConf, pt); - } catch (std::exception& ex) { - LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ()); - return; - } - - std::map > destinations; // keys -> destination - for (auto& section: pt) - { - std::string name = section.first; - try - { - std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); - if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT - || type == I2P_TUNNELS_SECTION_TYPE_SOCKS - || type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS - || type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY - || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) - { - // mandatory params - std::string dest; - if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) - dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); - int port = section.second.get (I2P_CLIENT_TUNNEL_PORT); - // optional params - bool matchTunnels = section.second.get(I2P_CLIENT_TUNNEL_MATCH_TUNNELS, false); - std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "transient"); - std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); - int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); - i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - // I2CP - std::map options; - ReadI2CPOptions (section, false, options); - - std::shared_ptr localDestination = nullptr; - if (keys.length () > 0) - { - auto it = destinations.find (keys); - if (it != destinations.end ()) - localDestination = it->second; - else - { - i2p::data::PrivateKeys k; - if(LoadPrivateKeys (k, keys, sigType, cryptoType)) - { - localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); - if (!localDestination) - { - if(matchTunnels) - localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); - else - localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); - if (keys != "transient") - destinations[keys] = localDestination; - } - } - } - } - - if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { - // udp client - // TODO: hostnames - boost::asio::ip::udp::endpoint end (boost::asio::ip::address::from_string(address), port); - if (!localDestination) - localDestination = m_SharedLocalDestination; - - bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true); - auto clientTunnel = std::make_shared (name, dest, end, localDestination, destinationPort, gzip); - - auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel)); - if (ins.second) - { - clientTunnel->Start (); - numClientTunnels++; - } - else - { - // TODO: update - if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) - { - LogPrint (eLogInfo, "Clients: I2P UDP client tunnel destination updated"); - ins.first->second->Stop (); - ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); - ins.first->second->Start (); - } - ins.first->second->isUpdated = true; - LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); - } - - } else { - boost::asio::ip::tcp::endpoint clientEndpoint; - std::shared_ptr clientTunnel; - if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS) - { - // socks proxy - std::string outproxy = section.second.get("outproxy", ""); - auto tun = std::make_shared(name, address, port, !outproxy.empty(), outproxy, destinationPort, localDestination); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint (); - } - else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY) - { - // http proxy - std::string outproxy = section.second.get("outproxy", ""); - bool addresshelper = section.second.get("addresshelper", true); - auto tun = std::make_shared(name, address, port, outproxy, addresshelper, localDestination); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint (); - } - else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) - { - LogPrint(eLogWarning, "Clients: I2P Client tunnel websocks is deprecated, not starting ", name, " tunnel"); - continue; - } - else - { - // tcp client - auto tun = std::make_shared (name, dest, address, port, localDestination, destinationPort); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint (); - - uint32_t keepAlive = section.second.get(I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL, 0); - if (keepAlive) - { - tun->SetKeepAliveInterval (keepAlive); - LogPrint(eLogInfo, "Clients: I2P Client tunnel keep alive interval set to ", keepAlive); - } - } - - uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); - if(timeout) - { - clientTunnel->SetConnectTimeout(timeout); - LogPrint(eLogInfo, "Clients: I2P Client tunnel connect timeout set to ", timeout); - } - - auto ins = m_ClientTunnels.insert (std::make_pair (clientEndpoint, clientTunnel)); - if (ins.second) - { - clientTunnel->Start (); - numClientTunnels++; - } - else - { - // TODO: update - if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) - { - LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated"); - ins.first->second->Stop (); - ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); - ins.first->second->Start (); - } - ins.first->second->isUpdated = true; - LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists"); - } - } - } - - else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER - || type == I2P_TUNNELS_SECTION_TYPE_HTTP - || type == I2P_TUNNELS_SECTION_TYPE_IRC - || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) - { - // mandatory params - std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); - int port = section.second.get (I2P_SERVER_TUNNEL_PORT); - std::string keys = section.second.get (I2P_SERVER_TUNNEL_KEYS); - // optional params - int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0); - std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); - if(accessList == "") - accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, ""); - std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); - std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); - bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, false); - i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - - std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, ""); - bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); - - // I2CP - std::map options; - ReadI2CPOptions (section, true, options); - - std::shared_ptr localDestination = nullptr; - auto it = destinations.find (keys); - if (it != destinations.end ()) - { - localDestination = it->second; - localDestination->SetPublic (true); - } - else - { - i2p::data::PrivateKeys k; - if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) - continue; - localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); - if (!localDestination) - { - localDestination = CreateNewLocalDestination (k, true, &options); - destinations[keys] = localDestination; - } - else - localDestination->SetPublic (true); - } - if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) - { - // udp server tunnel - // TODO: hostnames - boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); - if (address.empty ()) - { - if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ()) - address = "::1"; - else - address = "127.0.0.1"; - } - auto localAddress = boost::asio::ip::address::from_string(address); - auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip); - if(!isUniqueLocal) - { - LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); - serverTunnel->SetUniqueLocal(isUniqueLocal); - } - std::lock_guard lock(m_ForwardsMutex); - auto ins = m_ServerForwards.insert(std::make_pair( - std::make_pair(localDestination->GetIdentHash(), port), - serverTunnel)); - if (ins.second) - { - serverTunnel->Start(); - LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); - } - else - { - ins.first->second->isUpdated = true; - LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists"); - } - - continue; - } - - std::shared_ptr serverTunnel; - if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) - serverTunnel = std::make_shared (name, host, port, localDestination, hostOverride, inPort, gzip); - else if (type == I2P_TUNNELS_SECTION_TYPE_IRC) - serverTunnel = std::make_shared (name, host, port, localDestination, webircpass, inPort, gzip); - else // regular server tunnel by default - serverTunnel = std::make_shared (name, host, port, localDestination, inPort, gzip); - - if (!address.empty ()) - serverTunnel->SetLocalAddress (address); - if(!isUniqueLocal) - { - LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); - serverTunnel->SetUniqueLocal(isUniqueLocal); - } - if (accessList.length () > 0) - { - std::set idents; - size_t pos = 0, comma; - do - { - comma = accessList.find (',', pos); - i2p::data::IdentHash ident; - ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); - idents.insert (ident); - pos = comma + 1; - } - while (comma != std::string::npos); - serverTunnel->SetAccessList (idents); - } - auto ins = m_ServerTunnels.insert (std::make_pair ( - std::make_pair (localDestination->GetIdentHash (), inPort), - serverTunnel)); - if (ins.second) - { - serverTunnel->Start (); - numServerTunnels++; - } - else - { - // TODO: update - if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ()) - { - LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated"); - ins.first->second->Stop (); - ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ()); - ins.first->second->Start (); - } - ins.first->second->isUpdated = true; - LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); - } - - } - else - LogPrint (eLogWarning, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ()); - ThrowFatal ("Unable to start tunnel ", name, ": ", ex.what ()); - } - } - } - - void ClientContext::ReadHttpProxy () - { - std::shared_ptr localDestination; - bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy); - if (httproxy) - { - std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); - std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr); - uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); - std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); - bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); - if (httpAddresshelper) - i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book - i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); - LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); - if (httpProxyKeys.length () > 0) - { - i2p::data::PrivateKeys keys; - if(LoadPrivateKeys (keys, httpProxyKeys, sigType)) - { - std::map params; - ReadI2CPOptionsFromConfig ("httpproxy.", params); - localDestination = CreateNewLocalDestination (keys, false, ¶ms); - if (localDestination) localDestination->Acquire (); - } - else - LogPrint(eLogError, "Clients: Failed to load HTTP Proxy key"); - } - try - { - m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, httpAddresshelper, localDestination); - m_HttpProxy->Start(); - } - catch (std::exception& e) - { - LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what()); - ThrowFatal ("Unable to start HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort, ": ", e.what ()); - } - } - } - - void ClientContext::ReadSocksProxy () - { - std::shared_ptr localDestination; - bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy); - if (socksproxy) - { - std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); - // we still need httpProxyKeys to compare with sockProxyKeys - std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys); - std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); - uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); - bool socksOutProxy; i2p::config::GetOption("socksproxy.outproxy.enabled", socksOutProxy); - std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); - uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); - i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); - LogPrint(eLogInfo, "Clients: Starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); - if (httpProxyKeys == socksProxyKeys && m_HttpProxy) - { - localDestination = m_HttpProxy->GetLocalDestination (); - localDestination->Acquire (); - } - else if (socksProxyKeys.length () > 0) - { - i2p::data::PrivateKeys keys; - if (LoadPrivateKeys (keys, socksProxyKeys, sigType)) - { - std::map params; - ReadI2CPOptionsFromConfig ("socksproxy.", params); - localDestination = CreateNewLocalDestination (keys, false, ¶ms); - if (localDestination) localDestination->Acquire (); - } - else - LogPrint(eLogError, "Clients: Failed to load SOCKS Proxy key"); - } - try - { - m_SocksProxy = new i2p::proxy::SOCKSProxy("SOCKS", socksProxyAddr, socksProxyPort, - socksOutProxy, socksOutProxyAddr, socksOutProxyPort, localDestination); - m_SocksProxy->Start(); - } - catch (std::exception& e) - { - LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what()); - ThrowFatal ("Unable to start SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort, ": ", e.what ()); - } - } - } - - void ClientContext::ScheduleCleanupUDP() - { - if (m_CleanupUDPTimer) - { - // schedule cleanup in 17 seconds - m_CleanupUDPTimer->expires_from_now (boost::posix_time::seconds (17)); - m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1)); - } - } - - void ClientContext::CleanupUDP(const boost::system::error_code & ecode) - { - if(!ecode) - { - std::lock_guard lock(m_ForwardsMutex); - for (auto & s : m_ServerForwards ) s.second->ExpireStale(); - ScheduleCleanupUDP(); - } - } - - void ClientContext::VisitTunnels (bool clean) - { - for (auto it = m_ClientTunnels.begin (); it != m_ClientTunnels.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ClientTunnels.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - - for (auto it = m_ServerTunnels.begin (); it != m_ServerTunnels.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ServerTunnels.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - - // TODO: Write correct UDP tunnels stop - for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ClientForwards.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - - for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ServerForwards.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - } -} +namespace i2p { + namespace client { + ClientContext context; + + ClientContext::ClientContext() : m_SharedLocalDestination(nullptr), + m_HttpProxy(nullptr), m_SocksProxy(nullptr), m_SamBridge(nullptr), + m_BOBCommandChannel(nullptr), m_I2CPServer(nullptr) { + } + + ClientContext::~ClientContext() { + delete m_HttpProxy; + delete m_SocksProxy; + delete m_SamBridge; + delete m_BOBCommandChannel; + delete m_I2CPServer; + } + + void ClientContext::Start() { + // shared local destination + if (!m_SharedLocalDestination) + CreateNewSharedLocalDestination(); + + // addressbook + m_AddressBook.Start(); + + // HTTP proxy + ReadHttpProxy(); + + // SOCKS proxy + ReadSocksProxy(); + + // I2P tunnels + ReadTunnels(); + + // SAM + bool sam; + i2p::config::GetOption("sam.enabled", sam); + if (sam) { + std::string samAddr; + i2p::config::GetOption("sam.address", samAddr); + uint16_t samPort; + i2p::config::GetOption("sam.port", samPort); + bool singleThread; + i2p::config::GetOption("sam.singlethread", singleThread); + LogPrint(eLogInfo, "Clients: Starting SAM bridge at ", samAddr, ":", samPort); + try { + m_SamBridge = new SAMBridge(samAddr, samPort, singleThread); + m_SamBridge->Start(); + } + catch (std::exception &e) { + LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); + ThrowFatal("Unable to start SAM bridge at ", samAddr, ":", samPort, ": ", e.what()); + } + } + + // BOB + bool bob; + i2p::config::GetOption("bob.enabled", bob); + if (bob) { + std::string bobAddr; + i2p::config::GetOption("bob.address", bobAddr); + uint16_t bobPort; + i2p::config::GetOption("bob.port", bobPort); + LogPrint(eLogInfo, "Clients: Starting BOB command channel at ", bobAddr, ":", bobPort); + try { + m_BOBCommandChannel = new BOBCommandChannel(bobAddr, bobPort); + m_BOBCommandChannel->Start(); + } + catch (std::exception &e) { + LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what()); + ThrowFatal("Unable to start BOB bridge at ", bobAddr, ":", bobPort, ": ", e.what()); + } + } + + // I2CP + bool i2cp; + i2p::config::GetOption("i2cp.enabled", i2cp); + if (i2cp) { + std::string i2cpAddr; + i2p::config::GetOption("i2cp.address", i2cpAddr); + uint16_t i2cpPort; + i2p::config::GetOption("i2cp.port", i2cpPort); + bool singleThread; + i2p::config::GetOption("i2cp.singlethread", singleThread); + LogPrint(eLogInfo, "Clients: Starting I2CP at ", i2cpAddr, ":", i2cpPort); + try { + m_I2CPServer = new I2CPServer(i2cpAddr, i2cpPort, singleThread); + m_I2CPServer->Start(); + } + catch (std::exception &e) { + LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what()); + ThrowFatal("Unable to start I2CP at ", i2cpAddr, ":", i2cpPort, ": ", e.what()); + } + } + + m_AddressBook.StartResolvers(); + + // start UDP cleanup + if (!m_ServerForwards.empty()) { + m_CleanupUDPTimer.reset(new boost::asio::deadline_timer(m_SharedLocalDestination->GetService())); + ScheduleCleanupUDP(); + } + } + + void ClientContext::Stop() { + if (m_HttpProxy) { + LogPrint(eLogInfo, "Clients: Stopping HTTP Proxy"); + m_HttpProxy->Stop(); + delete m_HttpProxy; + m_HttpProxy = nullptr; + } + + if (m_SocksProxy) { + LogPrint(eLogInfo, "Clients: Stopping SOCKS Proxy"); + m_SocksProxy->Stop(); + delete m_SocksProxy; + m_SocksProxy = nullptr; + } + + for (auto &it: m_ClientTunnels) { + LogPrint(eLogInfo, "Clients: Stopping I2P client tunnel on port ", it.first); + it.second->Stop(); + } + m_ClientTunnels.clear(); + + for (auto &it: m_ServerTunnels) { + LogPrint(eLogInfo, "Clients: Stopping I2P server tunnel"); + it.second->Stop(); + } + m_ServerTunnels.clear(); + + if (m_SamBridge) { + LogPrint(eLogInfo, "Clients: Stopping SAM bridge"); + m_SamBridge->Stop(); + delete m_SamBridge; + m_SamBridge = nullptr; + } + + if (m_BOBCommandChannel) { + LogPrint(eLogInfo, "Clients: Stopping BOB command channel"); + m_BOBCommandChannel->Stop(); + delete m_BOBCommandChannel; + m_BOBCommandChannel = nullptr; + } + + if (m_I2CPServer) { + LogPrint(eLogInfo, "Clients: Stopping I2CP"); + m_I2CPServer->Stop(); + delete m_I2CPServer; + m_I2CPServer = nullptr; + } + + LogPrint(eLogInfo, "Clients: Stopping AddressBook"); + m_AddressBook.Stop(); + + { + std::lock_guard lock(m_ForwardsMutex); + m_ServerForwards.clear(); + m_ClientForwards.clear(); + } + + if (m_CleanupUDPTimer) { + m_CleanupUDPTimer->cancel(); + m_CleanupUDPTimer = nullptr; + } + + for (auto &it: m_Destinations) + it.second->Stop(); + m_Destinations.clear(); + + m_SharedLocalDestination->Release(); + m_SharedLocalDestination = nullptr; + } + + void ClientContext::ReloadConfig() { + // TODO: handle config changes + /*std::string config; i2p::config::GetOption("conf", config); + i2p::config::ParseConfig(config);*/ + + // change shared local destination + m_SharedLocalDestination->Release(); + CreateNewSharedLocalDestination(); + + // recreate HTTP proxy + if (m_HttpProxy) { + m_HttpProxy->Stop(); + delete m_HttpProxy; + m_HttpProxy = nullptr; + } + ReadHttpProxy(); + + // recreate SOCKS proxy + if (m_SocksProxy) { + m_SocksProxy->Stop(); + delete m_SocksProxy; + m_SocksProxy = nullptr; + } + ReadSocksProxy(); + + // handle tunnels + // reset isUpdated for each tunnel + VisitTunnels(false); + // reload tunnels + ReadTunnels(); + // delete not updated tunnels (not in config anymore) + VisitTunnels(true); + + // delete unused destinations + std::unique_lock l(m_DestinationsMutex); + for (auto it = m_Destinations.begin(); it != m_Destinations.end();) { + auto dest = it->second; + if (dest->GetRefCounter() > 0) ++it; // skip + else { + dest->Stop(); + it = m_Destinations.erase(it); + } + } + } + + bool ClientContext::LoadPrivateKeys(i2p::data::PrivateKeys &keys, const std::string &filename, + i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType) { + static const std::string transient("transient"); + if (!filename.compare(0, transient.length(), transient)) // starts with transient + { + keys = i2p::data::PrivateKeys::CreateRandomKeys(sigType, cryptoType); + LogPrint(eLogInfo, "Clients: New transient keys address ", + m_AddressBook.ToAddress(keys.GetPublic()->GetIdentHash()), " created"); + return true; + } + + bool success = true; + std::string fullPath = i2p::fs::DataDirPath(filename); + std::ifstream s(fullPath, std::ifstream::binary); + if (s.is_open()) { + s.seekg(0, std::ios::end); + size_t len = s.tellg(); + s.seekg(0, std::ios::beg); + uint8_t *buf = new uint8_t[len]; + s.read((char *) buf, len); + if (!keys.FromBuffer(buf, len)) { + LogPrint(eLogError, "Clients: Failed to load keyfile ", filename); + success = false; + } else + LogPrint(eLogInfo, "Clients: Local address ", + m_AddressBook.ToAddress(keys.GetPublic()->GetIdentHash()), " loaded"); + delete[] buf; + } else { + LogPrint(eLogError, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", + sigType, " crypto type ", cryptoType); + keys = i2p::data::PrivateKeys::CreateRandomKeys(sigType, cryptoType); + std::ofstream f(fullPath, std::ofstream::binary | std::ofstream::out); + size_t len = keys.GetFullLen(); + uint8_t *buf = new uint8_t[len]; + len = keys.ToBuffer(buf, len); + f.write((char *) buf, len); + delete[] buf; + + LogPrint(eLogInfo, "Clients: New private keys file ", fullPath, " for ", + m_AddressBook.ToAddress(keys.GetPublic()->GetIdentHash()), " created"); + } + return success; + } + + std::vector > + ClientContext::GetForwardInfosFor(const i2p::data::IdentHash &destination) { + std::vector > infos; + std::lock_guard lock(m_ForwardsMutex); + for (const auto &c: m_ClientForwards) { + if (c.second->IsLocalDestination(destination)) { + for (auto &i: c.second->GetSessions()) infos.push_back(i); + break; + } + } + for (const auto &s: m_ServerForwards) { + if (std::get<0>(s.first) == destination) { + for (auto &i: s.second->GetSessions()) infos.push_back(i); + break; + } + } + return infos; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination(bool isPublic, + i2p::data::SigningKeyType sigType, + i2p::data::CryptoKeyType cryptoType, + const std::map *params) { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys(sigType, cryptoType); + auto localDestination = std::make_shared(keys, isPublic, params); + AddLocalDestination(localDestination); + return localDestination; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination( + boost::asio::io_service &service, bool isPublic, + i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, + const std::map *params) { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys(sigType, cryptoType); + auto localDestination = std::make_shared(service, keys, isPublic, params); + AddLocalDestination(localDestination); + return localDestination; + } + + std::shared_ptr + ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string &name, + const std::map *params) { + auto localDestination = std::make_shared(keys, name, params); + AddLocalDestination(localDestination); + return localDestination; + } + + void ClientContext::AddLocalDestination(std::shared_ptr localDestination) { + std::unique_lock l(m_DestinationsMutex); + m_Destinations[localDestination->GetIdentHash()] = localDestination; + localDestination->Start(); + } + + void ClientContext::DeleteLocalDestination(std::shared_ptr destination) { + if (!destination) return; + auto it = m_Destinations.find(destination->GetIdentHash()); + if (it != m_Destinations.end()) { + auto d = it->second; + { + std::unique_lock l(m_DestinationsMutex); + m_Destinations.erase(it); + } + d->Stop(); + } + } + + std::shared_ptr + ClientContext::CreateNewLocalDestination(const i2p::data::PrivateKeys &keys, bool isPublic, + const std::map *params) { + auto it = m_Destinations.find(keys.GetPublic()->GetIdentHash()); + if (it != m_Destinations.end()) { + LogPrint(eLogWarning, "Clients: Local destination ", + m_AddressBook.ToAddress(keys.GetPublic()->GetIdentHash()), " exists"); + it->second->Start(); // make sure to start + return it->second; + } + auto localDestination = std::make_shared(keys, isPublic, params); + AddLocalDestination(localDestination); + return localDestination; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination(boost::asio::io_service &service, + const i2p::data::PrivateKeys &keys, + bool isPublic, + const std::map *params) { + auto it = m_Destinations.find(keys.GetPublic()->GetIdentHash()); + if (it != m_Destinations.end()) { + LogPrint(eLogWarning, "Clients: Local destination ", + m_AddressBook.ToAddress(keys.GetPublic()->GetIdentHash()), " exists"); + it->second->Start(); // make sure to start + return it->second; + } + auto localDestination = std::make_shared(service, keys, isPublic, params); + AddLocalDestination(localDestination); + return localDestination; + } + + void ClientContext::CreateNewSharedLocalDestination() { + std::map params + { + {I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "3"}, + {I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "3"}, + {I2CP_PARAM_LEASESET_TYPE, "3"}, + {I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4"} + }; + m_SharedLocalDestination = CreateNewLocalDestination(false, + i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, + ¶ms); // non-public, EDDSA + m_SharedLocalDestination->Acquire(); + } + + std::shared_ptr + ClientContext::FindLocalDestination(const i2p::data::IdentHash &destination) const { + auto it = m_Destinations.find(destination); + if (it != m_Destinations.end()) + return it->second; + return nullptr; + } + + template + std::string + ClientContext::GetI2CPOption(const Section §ion, const std::string &name, const Type &value) const { + return section.second.get(boost::property_tree::ptree::path_type(name, '/'), std::to_string(value)); + } + + template + std::string ClientContext::GetI2CPStringOption(const Section §ion, const std::string &name, + const std::string &value) const { + return section.second.get(boost::property_tree::ptree::path_type(name, '/'), value); + } + + template + void ClientContext::ReadI2CPOptionsGroup(const Section §ion, const std::string &group, + std::map &options) const { + for (auto it: section.second) { + if (it.first.length() >= group.length() && !it.first.compare(0, group.length(), group)) + options[it.first] = it.second.get_value(""); + } + } + + template + void ClientContext::ReadI2CPOptions(const Section §ion, bool isServer, + std::map &options) const { + options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption(section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, + DEFAULT_INBOUND_TUNNEL_LENGTH); + options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption(section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, + DEFAULT_OUTBOUND_TUNNEL_LENGTH); + options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption(section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, + DEFAULT_INBOUND_TUNNELS_QUANTITY); + options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption(section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, + DEFAULT_OUTBOUND_TUNNELS_QUANTITY); + options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption(section, + I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, + DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE); + options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption(section, + I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, + DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE); + options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption(section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); + options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, + DEFAULT_MIN_TUNNEL_LATENCY); + options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, + DEFAULT_MAX_TUNNEL_LATENCY); + options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, + I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, + DEFAULT_INITIAL_ACK_DELAY); + options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, + isServer ? DEFAULT_ANSWER_PINGS : false); + options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); + std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4"); + if (encType.length() > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; + std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); + if (privKey.length() > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; + auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0); + if (authType != "0") // auth is set + { + options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType; + if (authType == "1") // DH + ReadI2CPOptionsGroup(section, I2CP_PARAM_LEASESET_CLIENT_DH, options); + else if (authType == "2") // PSK + ReadI2CPOptionsGroup(section, I2CP_PARAM_LEASESET_CLIENT_PSK, options); + } + std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, ""); + if (explicitPeers.length() > 0) options[I2CP_PARAM_EXPLICIT_PEERS] = explicitPeers; + std::string ratchetInboundTags = GetI2CPStringOption(section, I2CP_PARAM_RATCHET_INBOUND_TAGS, ""); + if (ratchetInboundTags.length() > 0) options[I2CP_PARAM_RATCHET_INBOUND_TAGS] = ratchetInboundTags; + } + + void ClientContext::ReadI2CPOptionsFromConfig(const std::string &prefix, + std::map &options) const { + std::string value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value)) + options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value)) + options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, value)) + options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) + options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) + options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, value)) + options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value)) + options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) + options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_TYPE, value)) + options[I2CP_PARAM_LEASESET_TYPE] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value)) + options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty()) + options[I2CP_PARAM_LEASESET_PRIV_KEY] = value; + } + + void ClientContext::ReadTunnels() { + int numClientTunnels = 0, numServerTunnels = 0; + std::string tunConf; + i2p::config::GetOption("tunconf", tunConf); + if (tunConf.empty()) + tunConf = i2p::fs::DataDirPath("tunnels.conf"); + + LogPrint(eLogDebug, "Clients: Tunnels config file: ", tunConf); + ReadTunnels(tunConf, numClientTunnels, numServerTunnels); + + std::string tunDir; + i2p::config::GetOption("tunnelsdir", tunDir); + if (tunDir.empty()) + tunDir = i2p::fs::DataDirPath("tunnels.d"); + + if (i2p::fs::Exists(tunDir)) { + std::vector files; + if (i2p::fs::ReadDir(tunDir, files)) { + for (auto &it: files) { + if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf" + LogPrint(eLogDebug, "Clients: Tunnels extra config file: ", it); + ReadTunnels(it, numClientTunnels, numServerTunnels); + } + } + } + + LogPrint(eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created"); + LogPrint(eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); + } + + void ClientContext::ReadTunnels(const std::string &tunConf, int &numClientTunnels, int &numServerTunnels) { + boost::property_tree::ptree pt; + try { + boost::property_tree::read_ini(tunConf, pt); + } catch (std::exception &ex) { + LogPrint(eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what()); + return; + } + + std::map > destinations; // keys -> destination + for (auto §ion: pt) { + std::string name = section.first; + try { + std::string type = section.second.get(I2P_TUNNELS_SECTION_TYPE); + if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT + || type == I2P_TUNNELS_SECTION_TYPE_SOCKS + || type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS + || type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY + || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { + // mandatory params + std::string dest; + if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) + dest = section.second.get(I2P_CLIENT_TUNNEL_DESTINATION); + int port = section.second.get(I2P_CLIENT_TUNNEL_PORT); + // optional params + bool matchTunnels = section.second.get(I2P_CLIENT_TUNNEL_MATCH_TUNNELS, false); + std::string keys = section.second.get(I2P_CLIENT_TUNNEL_KEYS, "transient"); + std::string address = section.second.get(I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); + int destinationPort = section.second.get(I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); + i2p::data::SigningKeyType sigType = section.second.get(I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, + i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); + i2p::data::CryptoKeyType cryptoType = section.second.get(I2P_CLIENT_TUNNEL_CRYPTO_TYPE, + i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); + // I2CP + std::map options; + ReadI2CPOptions(section, false, options); + + std::shared_ptr localDestination = nullptr; + if (keys.length() > 0) { + auto it = destinations.find(keys); + if (it != destinations.end()) + localDestination = it->second; + else { + i2p::data::PrivateKeys k; + if (LoadPrivateKeys(k, keys, sigType, cryptoType)) { + localDestination = FindLocalDestination(k.GetPublic()->GetIdentHash()); + if (!localDestination) { + if (matchTunnels) + localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); + else + localDestination = CreateNewLocalDestination(k, type == + I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, + &options); + if (keys != "transient") + destinations[keys] = localDestination; + } + } + } + } + + if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { + // udp client + // TODO: hostnames + boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port); + if (!localDestination) + localDestination = m_SharedLocalDestination; + + bool gzip = section.second.get(I2P_CLIENT_TUNNEL_GZIP, true); + auto clientTunnel = std::make_shared(name, dest, end, localDestination, + destinationPort, gzip); + + auto ins = m_ClientForwards.insert(std::make_pair(end, clientTunnel)); + if (ins.second) { + clientTunnel->Start(); + numClientTunnels++; + } else { + // TODO: update + if (ins.first->second->GetLocalDestination() != clientTunnel->GetLocalDestination()) { + LogPrint(eLogInfo, "Clients: I2P UDP client tunnel destination updated"); + ins.first->second->Stop(); + ins.first->second->SetLocalDestination(clientTunnel->GetLocalDestination()); + ins.first->second->Start(); + } + ins.first->second->isUpdated = true; + LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, + " already exists"); + } + + } else { + boost::asio::ip::tcp::endpoint clientEndpoint; + std::shared_ptr clientTunnel; + if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS) { + // socks proxy + std::string outproxy = section.second.get("outproxy", ""); + auto tun = std::make_shared(name, address, port, + !outproxy.empty(), outproxy, + destinationPort, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint(); + } else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY) { + // http proxy + std::string outproxy = section.second.get("outproxy", ""); + bool addresshelper = section.second.get("addresshelper", true); + auto tun = std::make_shared(name, address, port, outproxy, + addresshelper, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint(); + } else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) { + LogPrint(eLogWarning, + "Clients: I2P Client tunnel websocks is deprecated, not starting ", name, + " tunnel"); + continue; + } else { + // tcp client + auto tun = std::make_shared(name, dest, address, port, + localDestination, destinationPort); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint(); + + uint32_t keepAlive = section.second.get(I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL, + 0); + if (keepAlive) { + tun->SetKeepAliveInterval(keepAlive); + LogPrint(eLogInfo, "Clients: I2P Client tunnel keep alive interval set to ", + keepAlive); + } + } + + uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); + if (timeout) { + clientTunnel->SetConnectTimeout(timeout); + LogPrint(eLogInfo, "Clients: I2P Client tunnel connect timeout set to ", timeout); + } + + auto ins = m_ClientTunnels.insert(std::make_pair(clientEndpoint, clientTunnel)); + if (ins.second) { + clientTunnel->Start(); + numClientTunnels++; + } else { + // TODO: update + if (ins.first->second->GetLocalDestination() != clientTunnel->GetLocalDestination()) { + LogPrint(eLogInfo, "Clients: I2P client tunnel destination updated"); + ins.first->second->Stop(); + ins.first->second->SetLocalDestination(clientTunnel->GetLocalDestination()); + ins.first->second->Start(); + } + ins.first->second->isUpdated = true; + LogPrint(eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, + " already exists"); + } + } + } else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER + || type == I2P_TUNNELS_SECTION_TYPE_HTTP + || type == I2P_TUNNELS_SECTION_TYPE_IRC + || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { + // mandatory params + std::string host = section.second.get(I2P_SERVER_TUNNEL_HOST); + int port = section.second.get(I2P_SERVER_TUNNEL_PORT); + std::string keys = section.second.get(I2P_SERVER_TUNNEL_KEYS); + // optional params + int inPort = section.second.get(I2P_SERVER_TUNNEL_INPORT, 0); + std::string accessList = section.second.get(I2P_SERVER_TUNNEL_ACCESS_LIST, ""); + if (accessList == "") + accessList = section.second.get(I2P_SERVER_TUNNEL_WHITE_LIST, ""); + std::string hostOverride = section.second.get(I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); + std::string webircpass = section.second.get(I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); + bool gzip = section.second.get(I2P_SERVER_TUNNEL_GZIP, false); + i2p::data::SigningKeyType sigType = section.second.get(I2P_SERVER_TUNNEL_SIGNATURE_TYPE, + i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); + i2p::data::CryptoKeyType cryptoType = section.second.get(I2P_CLIENT_TUNNEL_CRYPTO_TYPE, + i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); + + std::string address = section.second.get(I2P_SERVER_TUNNEL_ADDRESS, ""); + bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); + + // I2CP + std::map options; + ReadI2CPOptions(section, true, options); + + std::shared_ptr localDestination = nullptr; + auto it = destinations.find(keys); + if (it != destinations.end()) { + localDestination = it->second; + localDestination->SetPublic(true); + } else { + i2p::data::PrivateKeys k; + if (!LoadPrivateKeys(k, keys, sigType, cryptoType)) + continue; + localDestination = FindLocalDestination(k.GetPublic()->GetIdentHash()); + if (!localDestination) { + localDestination = CreateNewLocalDestination(k, true, &options); + destinations[keys] = localDestination; + } else + localDestination->SetPublic(true); + } + if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { + // udp server tunnel + // TODO: hostnames + boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); + if (address.empty()) { + if (!endpoint.address().is_unspecified() && endpoint.address().is_v6()) + address = "::1"; + else + address = "127.0.0.1"; + } + auto localAddress = boost::asio::ip::address::from_string(address); + auto serverTunnel = std::make_shared(name, localDestination, + localAddress, endpoint, port, + gzip); + if (!isUniqueLocal) { + LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); + serverTunnel->SetUniqueLocal(isUniqueLocal); + } + std::lock_guard lock(m_ForwardsMutex); + auto ins = m_ServerForwards.insert(std::make_pair( + std::make_pair(localDestination->GetIdentHash(), port), + serverTunnel)); + if (ins.second) { + serverTunnel->Start(); + LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", + port, " bound on ", address, " for ", + localDestination->GetIdentHash().ToBase32()); + } else { + ins.first->second->isUpdated = true; + LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", + m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, + " already exists"); + } + + continue; + } + + std::shared_ptr serverTunnel; + if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) + serverTunnel = std::make_shared(name, host, port, localDestination, + hostOverride, inPort, gzip); + else if (type == I2P_TUNNELS_SECTION_TYPE_IRC) + serverTunnel = std::make_shared(name, host, port, localDestination, + webircpass, inPort, gzip); + else // regular server tunnel by default + serverTunnel = std::make_shared(name, host, port, localDestination, inPort, + gzip); + + if (!address.empty()) + serverTunnel->SetLocalAddress(address); + if (!isUniqueLocal) { + LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); + serverTunnel->SetUniqueLocal(isUniqueLocal); + } + if (accessList.length() > 0) { + std::set idents; + size_t pos = 0, comma; + do { + comma = accessList.find(',', pos); + i2p::data::IdentHash ident; + ident.FromBase32(accessList.substr(pos, comma != std::string::npos ? comma - pos + : std::string::npos)); + idents.insert(ident); + pos = comma + 1; + } while (comma != std::string::npos); + serverTunnel->SetAccessList(idents); + } + auto ins = m_ServerTunnels.insert(std::make_pair( + std::make_pair(localDestination->GetIdentHash(), inPort), + serverTunnel)); + if (ins.second) { + serverTunnel->Start(); + numServerTunnels++; + } else { + // TODO: update + if (ins.first->second->GetLocalDestination() != serverTunnel->GetLocalDestination()) { + LogPrint(eLogInfo, "Clients: I2P server tunnel destination updated"); + ins.first->second->Stop(); + ins.first->second->SetLocalDestination(serverTunnel->GetLocalDestination()); + ins.first->second->Start(); + } + ins.first->second->isUpdated = true; + LogPrint(eLogInfo, "Clients: I2P server tunnel for destination/port ", + m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", inPort, + " already exists"); + } + + } else + LogPrint(eLogWarning, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf); + } + catch (std::exception &ex) { + LogPrint(eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what()); + ThrowFatal("Unable to start tunnel ", name, ": ", ex.what()); + } + } + } + + void ClientContext::ReadHttpProxy() { + std::shared_ptr localDestination; + bool httproxy; + i2p::config::GetOption("httpproxy.enabled", httproxy); + if (httproxy) { + std::string httpProxyKeys; + i2p::config::GetOption("httpproxy.keys", httpProxyKeys); + std::string httpProxyAddr; + i2p::config::GetOption("httpproxy.address", httpProxyAddr); + uint16_t httpProxyPort; + i2p::config::GetOption("httpproxy.port", httpProxyPort); + std::string httpOutProxyURL; + i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); + bool httpAddresshelper; + i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); + if (httpAddresshelper) + i2p::config::GetOption("addressbook.enabled", + httpAddresshelper); // addresshelper is not supported without address book + i2p::data::SigningKeyType sigType; + i2p::config::GetOption("httpproxy.signaturetype", sigType); + LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); + if (httpProxyKeys.length() > 0) { + i2p::data::PrivateKeys keys; + if (LoadPrivateKeys(keys, httpProxyKeys, sigType)) { + std::map params; + ReadI2CPOptionsFromConfig("httpproxy.", params); + localDestination = CreateNewLocalDestination(keys, false, ¶ms); + if (localDestination) localDestination->Acquire(); + } else + LogPrint(eLogError, "Clients: Failed to load HTTP Proxy key"); + } + try { + m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, + httpAddresshelper, localDestination); + m_HttpProxy->Start(); + } + catch (std::exception &e) { + LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what()); + ThrowFatal("Unable to start HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort, ": ", e.what()); + } + } + } + + void ClientContext::ReadSocksProxy() { + std::shared_ptr localDestination; + bool socksproxy; + i2p::config::GetOption("socksproxy.enabled", socksproxy); + if (socksproxy) { + std::string httpProxyKeys; + i2p::config::GetOption("httpproxy.keys", httpProxyKeys); + // we still need httpProxyKeys to compare with sockProxyKeys + std::string socksProxyKeys; + i2p::config::GetOption("socksproxy.keys", socksProxyKeys); + std::string socksProxyAddr; + i2p::config::GetOption("socksproxy.address", socksProxyAddr); + uint16_t socksProxyPort; + i2p::config::GetOption("socksproxy.port", socksProxyPort); + bool socksOutProxy; + i2p::config::GetOption("socksproxy.outproxy.enabled", socksOutProxy); + std::string socksOutProxyAddr; + i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); + uint16_t socksOutProxyPort; + i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); + i2p::data::SigningKeyType sigType; + i2p::config::GetOption("socksproxy.signaturetype", sigType); + LogPrint(eLogInfo, "Clients: Starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); + if (httpProxyKeys == socksProxyKeys && m_HttpProxy) { + localDestination = m_HttpProxy->GetLocalDestination(); + localDestination->Acquire(); + } else if (socksProxyKeys.length() > 0) { + i2p::data::PrivateKeys keys; + if (LoadPrivateKeys(keys, socksProxyKeys, sigType)) { + std::map params; + ReadI2CPOptionsFromConfig("socksproxy.", params); + localDestination = CreateNewLocalDestination(keys, false, ¶ms); + if (localDestination) localDestination->Acquire(); + } else + LogPrint(eLogError, "Clients: Failed to load SOCKS Proxy key"); + } + try { + m_SocksProxy = new i2p::proxy::SOCKSProxy("SOCKS", socksProxyAddr, socksProxyPort, + socksOutProxy, socksOutProxyAddr, socksOutProxyPort, + localDestination); + m_SocksProxy->Start(); + } + catch (std::exception &e) { + LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what()); + ThrowFatal("Unable to start SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort, ": ", e.what()); + } + } + } + + void ClientContext::ScheduleCleanupUDP() { + if (m_CleanupUDPTimer) { + // schedule cleanup in 17 seconds + m_CleanupUDPTimer->expires_from_now(boost::posix_time::seconds(17)); + m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1)); + } + } + + void ClientContext::CleanupUDP(const boost::system::error_code &ecode) { + if (!ecode) { + std::lock_guard lock(m_ForwardsMutex); + for (auto &s: m_ServerForwards) s.second->ExpireStale(); + ScheduleCleanupUDP(); + } + } + + void ClientContext::VisitTunnels(bool clean) { + for (auto it = m_ClientTunnels.begin(); it != m_ClientTunnels.end();) { + if (clean && !it->second->isUpdated) { + it->second->Stop(); + it = m_ClientTunnels.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } + + for (auto it = m_ServerTunnels.begin(); it != m_ServerTunnels.end();) { + if (clean && !it->second->isUpdated) { + it->second->Stop(); + it = m_ServerTunnels.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } + + // TODO: Write correct UDP tunnels stop + for (auto it = m_ClientForwards.begin(); it != m_ClientForwards.end();) { + if (clean && !it->second->isUpdated) { + it->second->Stop(); + it = m_ClientForwards.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } + + for (auto it = m_ServerForwards.begin(); it != m_ServerForwards.end();) { + if (clean && !it->second->isUpdated) { + it->second->Stop(); + it = m_ServerForwards.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } + } + } } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index d512a55b..59cb14f7 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -24,154 +24,197 @@ #include "AddressBook.h" #include "I18N_langs.h" -namespace i2p -{ -namespace client -{ - const char I2P_TUNNELS_SECTION_TYPE[] = "type"; - const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client"; - const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server"; - const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http"; - const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc"; - const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient"; - const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver"; - const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks"; - const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks"; - const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy"; - const char I2P_CLIENT_TUNNEL_PORT[] = "port"; - const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address"; - const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; - const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; - const char I2P_CLIENT_TUNNEL_GZIP[] = "gzip"; - const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; - const char I2P_CLIENT_TUNNEL_CRYPTO_TYPE[] = "cryptotype"; - const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; - const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; - const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; - const char I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL[] = "keepaliveinterval"; - const char I2P_SERVER_TUNNEL_HOST[] = "host"; - const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; - const char I2P_SERVER_TUNNEL_PORT[] = "port"; - const char I2P_SERVER_TUNNEL_KEYS[] = "keys"; - const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; - const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; - const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; - const char I2P_SERVER_TUNNEL_WHITE_LIST[] = "whitelist"; - const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; - const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; - const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; - const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; +namespace i2p { + namespace client { + const char I2P_TUNNELS_SECTION_TYPE[] = "type"; + const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client"; + const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server"; + const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http"; + const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc"; + const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient"; + const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver"; + const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks"; + const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks"; + const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy"; + const char I2P_CLIENT_TUNNEL_PORT[] = "port"; + const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address"; + const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; + const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; + const char I2P_CLIENT_TUNNEL_GZIP[] = "gzip"; + const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; + const char I2P_CLIENT_TUNNEL_CRYPTO_TYPE[] = "cryptotype"; + const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; + const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; + const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; + const char I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL[] = "keepaliveinterval"; + const char I2P_SERVER_TUNNEL_HOST[] = "host"; + const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; + const char I2P_SERVER_TUNNEL_PORT[] = "port"; + const char I2P_SERVER_TUNNEL_KEYS[] = "keys"; + const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; + const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; + const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; + const char I2P_SERVER_TUNNEL_WHITE_LIST[] = "whitelist"; + const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; + const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; + const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; + const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; - class ClientContext - { - public: + class ClientContext { + public: - ClientContext (); - ~ClientContext (); + ClientContext(); - void Start (); - void Stop (); + ~ClientContext(); - void ReloadConfig (); + void Start(); - std::shared_ptr GetSharedLocalDestination () const { return m_SharedLocalDestination; }; - std::shared_ptr CreateNewLocalDestination (bool isPublic = false, // transient - i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, - const std::map * params = nullptr); // used by SAM only - std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, - bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, - const std::map * params = nullptr); // same as previous but on external io_service - std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); - std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, - const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); // same as previous but on external io_service - std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, - const std::string & name, const std::map * params = nullptr); - void DeleteLocalDestination (std::shared_ptr destination); - std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; - bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, - i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); + void Stop(); - AddressBook& GetAddressBook () { return m_AddressBook; }; - const BOBCommandChannel * GetBOBCommandChannel () const { return m_BOBCommandChannel; }; - const SAMBridge * GetSAMBridge () const { return m_SamBridge; }; - const I2CPServer * GetI2CPServer () const { return m_I2CPServer; }; + void ReloadConfig(); - std::vector > GetForwardInfosFor(const i2p::data::IdentHash & destination); + std::shared_ptr GetSharedLocalDestination() const { return m_SharedLocalDestination; }; - // i18n - std::shared_ptr GetLanguage () { return m_Language; }; - void SetLanguage (const std::shared_ptr language) { m_Language = language; }; + std::shared_ptr CreateNewLocalDestination(bool isPublic = false, // transient + i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, + const std::map *params = nullptr); // used by SAM only + std::shared_ptr CreateNewLocalDestination(boost::asio::io_service &service, + bool isPublic = false, + i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, + const std::map *params = nullptr); // same as previous but on external io_service + std::shared_ptr + CreateNewLocalDestination(const i2p::data::PrivateKeys &keys, bool isPublic = true, + const std::map *params = nullptr); - private: + std::shared_ptr CreateNewLocalDestination(boost::asio::io_service &service, + const i2p::data::PrivateKeys &keys, + bool isPublic = true, + const std::map *params = nullptr); // same as previous but on external io_service + std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, + const std::string &name, + const std::map *params = nullptr); - void ReadTunnels (); - void ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels); - void ReadHttpProxy (); - void ReadSocksProxy (); - template - std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const; - template - std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value - template - void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map& options) const; - template - void ReadI2CPOptions (const Section& section, bool isServer, std::map& options) const; // for tunnels - void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const; // for HTTP and SOCKS proxy + void DeleteLocalDestination(std::shared_ptr destination); - void CleanupUDP(const boost::system::error_code & ecode); - void ScheduleCleanupUDP(); + std::shared_ptr FindLocalDestination(const i2p::data::IdentHash &destination) const; - void VisitTunnels (bool clean); + bool LoadPrivateKeys(i2p::data::PrivateKeys &keys, const std::string &filename, + i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - void CreateNewSharedLocalDestination (); - void AddLocalDestination (std::shared_ptr localDestination); + AddressBook &GetAddressBook() { return m_AddressBook; }; - private: + const BOBCommandChannel *GetBOBCommandChannel() const { return m_BOBCommandChannel; }; - std::mutex m_DestinationsMutex; - std::map > m_Destinations; - std::shared_ptr m_SharedLocalDestination; + const SAMBridge *GetSAMBridge() const { return m_SamBridge; }; - AddressBook m_AddressBook; + const I2CPServer *GetI2CPServer() const { return m_I2CPServer; }; - i2p::proxy::HTTPProxy * m_HttpProxy; - i2p::proxy::SOCKSProxy * m_SocksProxy; - std::map > m_ClientTunnels; // local endpoint -> tunnel - std::map, std::shared_ptr > m_ServerTunnels; // -> tunnel + std::vector > + GetForwardInfosFor(const i2p::data::IdentHash &destination); - std::mutex m_ForwardsMutex; - std::map > m_ClientForwards; // local endpoint -> udp tunnel - std::map, std::shared_ptr > m_ServerForwards; // -> udp tunnel + // i18n + std::shared_ptr GetLanguage() { return m_Language; }; - SAMBridge * m_SamBridge; - BOBCommandChannel * m_BOBCommandChannel; - I2CPServer * m_I2CPServer; + void SetLanguage(const std::shared_ptr language) { m_Language = language; }; - std::unique_ptr m_CleanupUDPTimer; + private: - // i18n - std::shared_ptr m_Language; + void ReadTunnels(); - public: + void ReadTunnels(const std::string &tunConf, int &numClientTunnels, int &numServerTunnels); - // for HTTP - const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; - const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; }; - const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; }; - const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; } - const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; } - const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; } - const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; } - }; + void ReadHttpProxy(); - extern ClientContext context; -} + void ReadSocksProxy(); + + template + std::string GetI2CPOption(const Section §ion, const std::string &name, const Type &value) const; + + template + std::string GetI2CPStringOption(const Section §ion, const std::string &name, + const std::string &value) const; // GetI2CPOption with string default value + template + void ReadI2CPOptionsGroup(const Section §ion, const std::string &group, + std::map &options) const; + + template + void ReadI2CPOptions(const Section §ion, bool isServer, + std::map &options) const; // for tunnels + void ReadI2CPOptionsFromConfig(const std::string &prefix, + std::map &options) const; // for HTTP and SOCKS proxy + + void CleanupUDP(const boost::system::error_code &ecode); + + void ScheduleCleanupUDP(); + + void VisitTunnels(bool clean); + + void CreateNewSharedLocalDestination(); + + void AddLocalDestination(std::shared_ptr localDestination); + + private: + + std::mutex m_DestinationsMutex; + std::map > m_Destinations; + std::shared_ptr m_SharedLocalDestination; + + AddressBook m_AddressBook; + + i2p::proxy::HTTPProxy *m_HttpProxy; + i2p::proxy::SOCKSProxy *m_SocksProxy; + std::map > m_ClientTunnels; // local endpoint -> tunnel + std::map , std::shared_ptr> m_ServerTunnels; // -> tunnel + + std::mutex m_ForwardsMutex; + std::map > m_ClientForwards; // local endpoint -> udp tunnel + std::map , std::shared_ptr> m_ServerForwards; // -> udp tunnel + + SAMBridge *m_SamBridge; + BOBCommandChannel *m_BOBCommandChannel; + I2CPServer *m_I2CPServer; + + std::unique_ptr m_CleanupUDPTimer; + + // i18n + std::shared_ptr m_Language; + + public: + + // for HTTP + const decltype(m_Destinations) + & + + GetDestinations() const { return m_Destinations; }; + const decltype(m_ClientTunnels) + & + + GetClientTunnels() const { return m_ClientTunnels; }; + const decltype(m_ServerTunnels) + & + + GetServerTunnels() const { return m_ServerTunnels; }; + const decltype(m_ClientForwards) + & + + GetClientForwards() const { return m_ClientForwards; } + + const decltype(m_ServerForwards) + & + + GetServerForwards() const { return m_ServerForwards; } + + const i2p::proxy::HTTPProxy *GetHttpProxy() const { return m_HttpProxy; } + + const i2p::proxy::SOCKSProxy *GetSocksProxy() const { return m_SocksProxy; } + }; + + extern ClientContext context; + } } #endif diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 73677836..bd2b8a77 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -31,646 +31,656 @@ #include "I18N.h" namespace i2p { -namespace proxy { - static const std::vector jumporder = { - "reg.i2p", - "stats.i2p", - "identiguy.i2p", - }; + namespace proxy { + static const std::vector jumporder = { + "reg.i2p", + "stats.i2p", + "identiguy.i2p", + }; - static const std::map jumpservices = { - { "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" }, - { "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" }, - { "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" }, - }; + static const std::map jumpservices = { + {"reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/"}, + {"identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname="}, + {"stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a="}, + }; - static const char *pageHead = - "\r\n" - " \r\n" - " I2Pd HTTP proxy\r\n" - " \r\n" - "\r\n" - ; + static const char *pageHead = + "\r\n" + " \r\n" + " I2Pd HTTP proxy\r\n" + " \r\n" + "\r\n"; - bool str_rmatch(std::string & str, const char *suffix) { - auto pos = str.rfind (suffix); - if (pos == std::string::npos) - return false; /* not found */ - if (str.length() == (pos + std::strlen(suffix))) - return true; /* match */ - return false; - } + bool str_rmatch(std::string &str, const char *suffix) { + auto pos = str.rfind(suffix); + if (pos == std::string::npos) + return false; /* not found */ + if (str.length() == (pos + std::strlen(suffix))) + return true; /* match */ + return false; + } - class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this - { - private: + class HTTPReqHandler + : public i2p::client::I2PServiceHandler, public std::enable_shared_from_this { + private: - bool HandleRequest(); - void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void Terminate(); - void AsyncSockRead(); - bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm); - void SanitizeHTTPRequest(i2p::http::HTTPReq & req); - void SentHTTPFailed(const boost::system::error_code & ecode); - void HandleStreamRequestComplete (std::shared_ptr stream); - /* error helpers */ - void GenericProxyError(const std::string& title, const std::string& description); - void GenericProxyInfo(const std::string& title, const std::string& description); - void HostNotFound(std::string & host); - void SendProxyError(std::string & content); + bool HandleRequest(); - void ForwardToUpstreamProxy(); - void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); - void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); - void HTTPConnect(const std::string & host, uint16_t port); - void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); + void HandleSockRecv(const boost::system::error_code &ecode, std::size_t bytes_transfered); - void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered); - void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered); + void Terminate(); - typedef std::function ProxyResolvedHandler; + void AsyncSockRead(); - void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); + bool ExtractAddressHelper(i2p::http::URL &url, std::string &b64, bool &confirm); - void SocksProxySuccess(); - void HandoverToUpstreamProxy(); + void SanitizeHTTPRequest(i2p::http::HTTPReq &req); - uint8_t m_recv_chunk[8192]; - std::string m_recv_buf; // from client - std::string m_send_buf; // to upstream - std::shared_ptr m_sock; - std::shared_ptr m_proxysock; - boost::asio::ip::tcp::resolver m_proxy_resolver; - std::string m_OutproxyUrl; - bool m_Addresshelper; - i2p::http::URL m_ProxyURL; - i2p::http::URL m_RequestURL; - uint8_t m_socks_buf[255+8]; // for socks request/response - ssize_t m_req_len; - i2p::http::URL m_ClientRequestURL; - i2p::http::HTTPReq m_ClientRequest; - i2p::http::HTTPRes m_ClientResponse; - std::stringstream m_ClientRequestBuffer; + void SentHTTPFailed(const boost::system::error_code &ecode); - public: + void HandleStreamRequestComplete(std::shared_ptr stream); - HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock), - m_proxysock(std::make_shared(parent->GetService())), - m_proxy_resolver(parent->GetService()), - m_OutproxyUrl(parent->GetOutproxyURL()), - m_Addresshelper(parent->GetHelperSupport()) {} - ~HTTPReqHandler() { Terminate(); } - void Handle () { AsyncSockRead(); } /* overload */ - }; + /* error helpers */ + void GenericProxyError(const std::string &title, const std::string &description); - void HTTPReqHandler::AsyncSockRead() - { - LogPrint(eLogDebug, "HTTPProxy: Async sock read"); - if (!m_sock) { - LogPrint(eLogError, "HTTPProxy: No socket for read"); - return; - } - m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)), - std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } + void GenericProxyInfo(const std::string &title, const std::string &description); - void HTTPReqHandler::Terminate() { - if (Kill()) return; - if (m_sock) - { - LogPrint(eLogDebug, "HTTPProxy: Close sock"); - m_sock->close(); - m_sock = nullptr; - } - if(m_proxysock) - { - LogPrint(eLogDebug, "HTTPProxy: Close proxysock"); - if(m_proxysock->is_open()) - m_proxysock->close(); - m_proxysock = nullptr; - } - Done(shared_from_this()); - } + void HostNotFound(std::string &host); - void HTTPReqHandler::GenericProxyError(const std::string& title, const std::string& description) { - std::stringstream ss; - ss << "

" << tr("Proxy error") << ": " << title << "

\r\n"; - ss << "

" << description << "

\r\n"; - std::string content = ss.str(); - SendProxyError(content); - } + void SendProxyError(std::string &content); - void HTTPReqHandler::GenericProxyInfo(const std::string& title, const std::string& description) { - std::stringstream ss; - ss << "

" << tr("Proxy info") << ": " << title << "

\r\n"; - ss << "

" << description << "

\r\n"; - std::string content = ss.str(); - SendProxyError(content); - } + void ForwardToUpstreamProxy(); - void HTTPReqHandler::HostNotFound(std::string & host) { - std::stringstream ss; - ss << "

" << tr("Proxy error: Host not found") << "

\r\n" - << "

" << tr("Remote host not found in router's addressbook") << "

\r\n" - << "

" << tr("You may try to find this host on jump services below") << ":

\r\n" - << "\r\n"; - std::string content = ss.str(); - SendProxyError(content); - } + void HandleUpstreamHTTPProxyConnect(const boost::system::error_code &ec); - void HTTPReqHandler::SendProxyError(std::string & content) - { - i2p::http::HTTPRes res; - res.code = 500; - res.add_header("Content-Type", "text/html; charset=UTF-8"); - res.add_header("Connection", "close"); - std::stringstream ss; - ss << "\r\n" << pageHead - << "" << content << "\r\n" - << "\r\n"; - res.body = ss.str(); - std::string response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), - std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); - } + void HandleUpstreamSocksProxyConnect(const boost::system::error_code &ec); - bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm) - { - confirm = false; - const char *param = "i2paddresshelper="; - std::size_t pos = url.query.find(param); - std::size_t len = std::strlen(param); - std::map params; + void HTTPConnect(const std::string &host, uint16_t port); + + void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); + + void HandleSocksProxySendHandshake(const boost::system::error_code &ec, std::size_t bytes_transfered); + + void HandleSocksProxyReply(const boost::system::error_code &ec, std::size_t bytes_transfered); + + typedef std::function ProxyResolvedHandler; + + void HandleUpstreamProxyResolved(const boost::system::error_code &ecode, + boost::asio::ip::tcp::resolver::iterator itr, + ProxyResolvedHandler handler); + + void SocksProxySuccess(); + + void HandoverToUpstreamProxy(); + + uint8_t m_recv_chunk[8192]; + std::string m_recv_buf; // from client + std::string m_send_buf; // to upstream + std::shared_ptr m_sock; + std::shared_ptr m_proxysock; + boost::asio::ip::tcp::resolver m_proxy_resolver; + std::string m_OutproxyUrl; + bool m_Addresshelper; + i2p::http::URL m_ProxyURL; + i2p::http::URL m_RequestURL; + uint8_t m_socks_buf[255 + 8]; // for socks request/response + ssize_t m_req_len; + i2p::http::URL m_ClientRequestURL; + i2p::http::HTTPReq m_ClientRequest; + i2p::http::HTTPRes m_ClientResponse; + std::stringstream m_ClientRequestBuffer; + + public: + + HTTPReqHandler(HTTPProxy *parent, std::shared_ptr sock) : + I2PServiceHandler(parent), m_sock(sock), + m_proxysock(std::make_shared(parent->GetService())), + m_proxy_resolver(parent->GetService()), + m_OutproxyUrl(parent->GetOutproxyURL()), + m_Addresshelper(parent->GetHelperSupport()) {} + + ~HTTPReqHandler() { Terminate(); } + + void Handle() { AsyncSockRead(); } /* overload */ + }; + + void HTTPReqHandler::AsyncSockRead() { + LogPrint(eLogDebug, "HTTPProxy: Async sock read"); + if (!m_sock) { + LogPrint(eLogError, "HTTPProxy: No socket for read"); + return; + } + m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)), + std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + + void HTTPReqHandler::Terminate() { + if (Kill()) return; + if (m_sock) { + LogPrint(eLogDebug, "HTTPProxy: Close sock"); + m_sock->close(); + m_sock = nullptr; + } + if (m_proxysock) { + LogPrint(eLogDebug, "HTTPProxy: Close proxysock"); + if (m_proxysock->is_open()) + m_proxysock->close(); + m_proxysock = nullptr; + } + Done(shared_from_this()); + } + + void HTTPReqHandler::GenericProxyError(const std::string &title, const std::string &description) { + std::stringstream ss; + ss << "

" << tr("Proxy error") << ": " << title << "

\r\n"; + ss << "

" << description << "

\r\n"; + std::string content = ss.str(); + SendProxyError(content); + } + + void HTTPReqHandler::GenericProxyInfo(const std::string &title, const std::string &description) { + std::stringstream ss; + ss << "

" << tr("Proxy info") << ": " << title << "

\r\n"; + ss << "

" << description << "

\r\n"; + std::string content = ss.str(); + SendProxyError(content); + } + + void HTTPReqHandler::HostNotFound(std::string &host) { + std::stringstream ss; + ss << "

" << tr("Proxy error: Host not found") << "

\r\n" + << "

" << tr("Remote host not found in router's addressbook") << "

\r\n" + << "

" << tr("You may try to find this host on jump services below") << ":

\r\n" + << "\r\n"; + std::string content = ss.str(); + SendProxyError(content); + } + + void HTTPReqHandler::SendProxyError(std::string &content) { + i2p::http::HTTPRes res; + res.code = 500; + res.add_header("Content-Type", "text/html; charset=UTF-8"); + res.add_header("Connection", "close"); + std::stringstream ss; + ss << "\r\n" << pageHead + << "" << content << "\r\n" + << "\r\n"; + res.body = ss.str(); + std::string response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), + std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), + std::placeholders::_1)); + } + + bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL &url, std::string &b64, bool &confirm) { + confirm = false; + const char *param = "i2paddresshelper="; + std::size_t pos = url.query.find(param); + std::size_t len = std::strlen(param); + std::map params; - if (pos == std::string::npos) - return false; /* not found */ - if (!url.parse_query(params)) - return false; + if (pos == std::string::npos) + return false; /* not found */ + if (!url.parse_query(params)) + return false; - std::string value = params["i2paddresshelper"]; - len += value.length(); - b64 = i2p::http::UrlDecode(value); - // if we need update exists, request formed with update param - if (params["update"] == "true") { len += std::strlen("&update=true"); confirm = true; } - if (pos != 0 && url.query[pos-1] == '&') { pos--; len++; } // if helper is not only one query option - url.query.replace(pos, len, ""); - return true; - } + std::string value = params["i2paddresshelper"]; + len += value.length(); + b64 = i2p::http::UrlDecode(value); + // if we need update exists, request formed with update param + if (params["update"] == "true") { + len += std::strlen("&update=true"); + confirm = true; + } + if (pos != 0 && url.query[pos - 1] == '&') { + pos--; + len++; + } // if helper is not only one query option + url.query.replace(pos, len, ""); + return true; + } - void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req) - { - /* drop common headers */ - req.RemoveHeader("Via"); - req.RemoveHeader("From"); - req.RemoveHeader("Forwarded"); - req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding - /* drop proxy-disclosing headers */ - req.RemoveHeader("X-Forwarded"); - req.RemoveHeader("Proxy-"); // Proxy-* - /* replace headers */ - req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); + void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq &req) { + /* drop common headers */ + req.RemoveHeader("Via"); + req.RemoveHeader("From"); + req.RemoveHeader("Forwarded"); + req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding + /* drop proxy-disclosing headers */ + req.RemoveHeader("X-Forwarded"); + req.RemoveHeader("Proxy-"); // Proxy-* + /* replace headers */ + req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); - /** - * according to i2p ticket #1862: - * leave Referer if requested URL with same schema, host and port, - * otherwise, drop it. - */ - if(req.GetHeader("Referer") != "") { - i2p::http::URL reqURL; reqURL.parse(req.uri); - i2p::http::URL refURL; refURL.parse(req.GetHeader("Referer")); - if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port) - req.RemoveHeader("Referer"); - } + /** + * according to i2p ticket #1862: + * leave Referer if requested URL with same schema, host and port, + * otherwise, drop it. + */ + if (req.GetHeader("Referer") != "") { + i2p::http::URL reqURL; + reqURL.parse(req.uri); + i2p::http::URL refURL; + refURL.parse(req.GetHeader("Referer")); + if (!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || + reqURL.port != refURL.port) + req.RemoveHeader("Referer"); + } - /* add headers */ - /* close connection, if not Connection: (U|u)pgrade (for websocket) */ - auto h = req.GetHeader ("Connection"); - auto x = h.find("pgrade"); - if (!(x != std::string::npos && std::tolower(h[x - 1]) == 'u')) - req.UpdateHeader("Connection", "close"); - } + /* add headers */ + /* close connection, if not Connection: (U|u)pgrade (for websocket) */ + auto h = req.GetHeader("Connection"); + auto x = h.find("pgrade"); + if (!(x != std::string::npos && std::tolower(h[x - 1]) == 'u')) + req.UpdateHeader("Connection", "close"); + } - /** - * @brief Try to parse request from @a m_recv_buf - * If parsing success, rebuild request and store to @a m_send_buf - * with remaining data tail - * @return true on processed request or false if more data needed - */ - bool HTTPReqHandler::HandleRequest() - { - m_req_len = m_ClientRequest.parse(m_recv_buf); + /** + * @brief Try to parse request from @a m_recv_buf + * If parsing success, rebuild request and store to @a m_send_buf + * with remaining data tail + * @return true on processed request or false if more data needed + */ + bool HTTPReqHandler::HandleRequest() { + m_req_len = m_ClientRequest.parse(m_recv_buf); - if (m_req_len == 0) - return false; /* need more data */ + if (m_req_len == 0) + return false; /* need more data */ - if (m_req_len < 0) { - LogPrint(eLogError, "HTTPProxy: Unable to parse request"); - GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request")); - return true; /* parse error */ - } + if (m_req_len < 0) { + LogPrint(eLogError, "HTTPProxy: Unable to parse request"); + GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request")); + return true; /* parse error */ + } - /* parsing success, now let's look inside request */ - LogPrint(eLogDebug, "HTTPProxy: Requested: ", m_ClientRequest.uri); - m_RequestURL.parse(m_ClientRequest.uri); - bool m_Confirm; + /* parsing success, now let's look inside request */ + LogPrint(eLogDebug, "HTTPProxy: Requested: ", m_ClientRequest.uri); + m_RequestURL.parse(m_ClientRequest.uri); + bool m_Confirm; - std::string jump; - if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm)) - { - if (!m_Addresshelper) - { - LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); - GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); - return true; - } - if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) - { - i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); - LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); - std::string full_url = m_RequestURL.to_string(); - std::stringstream ss; - ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". "; - ss << tr("Click here to proceed:") << " " << tr("Continue") << "."; - GenericProxyInfo(tr("Addresshelper found"), ss.str()); - return true; /* request processed */ - } - else - { - std::string full_url = m_RequestURL.to_string(); - std::stringstream ss; - ss << tr("Host") << " " << m_RequestURL.host << " " << tr("already in router's addressbook") << ". "; - ss << tr(/* tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. */ "Click here to update record:" ) << " " << tr("Continue") << "."; - GenericProxyInfo(tr("Addresshelper found"), ss.str()); - return true; /* request processed */ - } - } - std::string dest_host; - uint16_t dest_port; - bool useConnect = false; - if(m_ClientRequest.method == "CONNECT") - { - std::string uri(m_ClientRequest.uri); - auto pos = uri.find(":"); - if(pos == std::string::npos || pos == uri.size() - 1) - { - GenericProxyError(tr("Invalid request"), tr("invalid request uri")); - return true; - } - else - { - useConnect = true; - dest_port = std::stoi(uri.substr(pos+1)); - dest_host = uri.substr(0, pos); - } - } - else - { - SanitizeHTTPRequest(m_ClientRequest); + std::string jump; + if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm)) { + if (!m_Addresshelper) { + LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); + GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); + return true; + } + if (!i2p::client::context.GetAddressBook().FindAddress(m_RequestURL.host) || m_Confirm) { + i2p::client::context.GetAddressBook().InsertAddress(m_RequestURL.host, jump); + LogPrint(eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); + std::string full_url = m_RequestURL.to_string(); + std::stringstream ss; + ss << tr("Host") << " " << m_RequestURL.host << " " + << tr("added to router's addressbook from helper") << ". "; + ss << tr("Click here to proceed:") << " " << tr("Continue") + << "."; + GenericProxyInfo(tr("Addresshelper found"), ss.str()); + return true; /* request processed */ + } else { + std::string full_url = m_RequestURL.to_string(); + std::stringstream ss; + ss << tr("Host") << " " << m_RequestURL.host << " " + << tr("already in router's addressbook") << ". "; + ss + << tr(/* tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. */ + "Click here to update record:") << " " << tr("Continue") << "."; + GenericProxyInfo(tr("Addresshelper found"), ss.str()); + return true; /* request processed */ + } + } + std::string dest_host; + uint16_t dest_port; + bool useConnect = false; + if (m_ClientRequest.method == "CONNECT") { + std::string uri(m_ClientRequest.uri); + auto pos = uri.find(":"); + if (pos == std::string::npos || pos == uri.size() - 1) { + GenericProxyError(tr("Invalid request"), tr("invalid request uri")); + return true; + } else { + useConnect = true; + dest_port = std::stoi(uri.substr(pos + 1)); + dest_host = uri.substr(0, pos); + } + } else { + SanitizeHTTPRequest(m_ClientRequest); - dest_host = m_RequestURL.host; - dest_port = m_RequestURL.port; - /* always set port, even if missing in request */ - if (!dest_port) - dest_port = (m_RequestURL.schema == "https") ? 443 : 80; - /* detect dest_host, set proper 'Host' header in upstream request */ - if (dest_host != "") - { - /* absolute url, replace 'Host' header */ - std::string h = dest_host; - if (dest_port != 0 && dest_port != 80) - h += ":" + std::to_string(dest_port); - m_ClientRequest.UpdateHeader("Host", h); - } - else - { - auto h = m_ClientRequest.GetHeader ("Host"); - if (h.length () > 0) - { - /* relative url and 'Host' header provided. transparent proxy mode? */ - i2p::http::URL u; - std::string t = "http://" + h; - u.parse(t); - dest_host = u.host; - dest_port = u.port; - } - else - { - /* relative url and missing 'Host' header */ - GenericProxyError(tr("Invalid request"), tr("Can't detect destination host from request")); - return true; - } - } - } - /* check dest_host really exists and inside I2P network */ - if (str_rmatch(dest_host, ".i2p")) { - if (!i2p::client::context.GetAddressBook ().GetAddress (dest_host)) { - HostNotFound(dest_host); - return true; /* request processed */ - } - } else { - if(m_OutproxyUrl.size()) { - LogPrint (eLogDebug, "HTTPProxy: Using outproxy ", m_OutproxyUrl); - if(m_ProxyURL.parse(m_OutproxyUrl)) - ForwardToUpstreamProxy(); - else - GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings")); - } else { - LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); - std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled"); - GenericProxyError(tr("Outproxy failure"), ss.str()); - } - return true; - } - if(useConnect) - { - HTTPConnect(dest_host, dest_port); - return true; - } + dest_host = m_RequestURL.host; + dest_port = m_RequestURL.port; + /* always set port, even if missing in request */ + if (!dest_port) + dest_port = (m_RequestURL.schema == "https") ? 443 : 80; + /* detect dest_host, set proper 'Host' header in upstream request */ + if (dest_host != "") { + /* absolute url, replace 'Host' header */ + std::string h = dest_host; + if (dest_port != 0 && dest_port != 80) + h += ":" + std::to_string(dest_port); + m_ClientRequest.UpdateHeader("Host", h); + } else { + auto h = m_ClientRequest.GetHeader("Host"); + if (h.length() > 0) { + /* relative url and 'Host' header provided. transparent proxy mode? */ + i2p::http::URL u; + std::string t = "http://" + h; + u.parse(t); + dest_host = u.host; + dest_port = u.port; + } else { + /* relative url and missing 'Host' header */ + GenericProxyError(tr("Invalid request"), tr("Can't detect destination host from request")); + return true; + } + } + } + /* check dest_host really exists and inside I2P network */ + if (str_rmatch(dest_host, ".i2p")) { + if (!i2p::client::context.GetAddressBook().GetAddress(dest_host)) { + HostNotFound(dest_host); + return true; /* request processed */ + } + } else { + if (m_OutproxyUrl.size()) { + LogPrint(eLogDebug, "HTTPProxy: Using outproxy ", m_OutproxyUrl); + if (m_ProxyURL.parse(m_OutproxyUrl)) + ForwardToUpstreamProxy(); + else + GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings")); + } else { + LogPrint(eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); + std::stringstream ss; + ss << tr("Host") << " " << dest_host << " " + << tr("not inside I2P network, but outproxy is not enabled"); + GenericProxyError(tr("Outproxy failure"), ss.str()); + } + return true; + } + if (useConnect) { + HTTPConnect(dest_host, dest_port); + return true; + } - /* make relative url */ - m_RequestURL.schema = ""; - m_RequestURL.host = ""; - m_ClientRequest.uri = m_RequestURL.to_string(); + /* make relative url */ + m_RequestURL.schema = ""; + m_RequestURL.host = ""; + m_ClientRequest.uri = m_RequestURL.to_string(); - /* drop original request from recv buffer */ - m_recv_buf.erase(0, m_req_len); - /* build new buffer from modified request and data from original request */ - m_send_buf = m_ClientRequest.to_string(); - m_send_buf.append(m_recv_buf); - /* connect to destination */ - LogPrint(eLogDebug, "HTTPProxy: Connecting to host ", dest_host, ":", dest_port); - GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), dest_host, dest_port); - return true; - } + /* drop original request from recv buffer */ + m_recv_buf.erase(0, m_req_len); + /* build new buffer from modified request and data from original request */ + m_send_buf = m_ClientRequest.to_string(); + m_send_buf.append(m_recv_buf); + /* connect to destination */ + LogPrint(eLogDebug, "HTTPProxy: Connecting to host ", dest_host, ":", dest_port); + GetOwner()->CreateStream(std::bind(&HTTPReqHandler::HandleStreamRequestComplete, + shared_from_this(), std::placeholders::_1), dest_host, dest_port); + return true; + } - void HTTPReqHandler::ForwardToUpstreamProxy() - { - LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream"); + void HTTPReqHandler::ForwardToUpstreamProxy() { + LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream"); - /* build http request */ - m_ClientRequestURL = m_RequestURL; - LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); - m_ClientRequestURL.schema = ""; - m_ClientRequestURL.host = ""; - std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? - m_ClientRequest.uri = m_ClientRequestURL.to_string(); + /* build http request */ + m_ClientRequestURL = m_RequestURL; + LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); + m_ClientRequestURL.schema = ""; + m_ClientRequestURL.host = ""; + std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? + m_ClientRequest.uri = m_ClientRequestURL.to_string(); - /* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */ - if(m_ClientRequest.method != "CONNECT") - m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"); + /* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */ + if (m_ClientRequest.method != "CONNECT") + m_ClientRequest.UpdateHeader("User-Agent", + "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"); - m_ClientRequest.write(m_ClientRequestBuffer); - m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); + m_ClientRequest.write(m_ClientRequestBuffer); + m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); - /* assume http if empty schema */ - if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") - { - /* handle upstream http proxy */ - if (!m_ProxyURL.port) m_ProxyURL.port = 80; - if (m_ProxyURL.is_i2p()) - { - m_ClientRequest.uri = origURI; - auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass); - if (!auth.empty ()) - { - /* remove existing authorization if any */ - m_ClientRequest.RemoveHeader("Proxy-"); - /* add own http proxy authorization */ - m_ClientRequest.AddHeader("Proxy-Authorization", auth); - } - m_send_buf = m_ClientRequest.to_string(); - m_recv_buf.erase(0, m_req_len); - m_send_buf.append(m_recv_buf); - GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_ProxyURL.host, m_ProxyURL.port); - } - else - { - boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); - m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); - })); - } - } - else if (m_ProxyURL.schema == "socks") - { - /* handle upstream socks proxy */ - if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified - boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); - m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); - })); - } - else - { - /* unknown type, complain */ - GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); - } - } + /* assume http if empty schema */ + if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { + /* handle upstream http proxy */ + if (!m_ProxyURL.port) m_ProxyURL.port = 80; + if (m_ProxyURL.is_i2p()) { + m_ClientRequest.uri = origURI; + auto auth = i2p::http::CreateBasicAuthorizationString(m_ProxyURL.user, m_ProxyURL.pass); + if (!auth.empty()) { + /* remove existing authorization if any */ + m_ClientRequest.RemoveHeader("Proxy-"); + /* add own http proxy authorization */ + m_ClientRequest.AddHeader("Proxy-Authorization", auth); + } + m_send_buf = m_ClientRequest.to_string(); + m_recv_buf.erase(0, m_req_len); + m_send_buf.append(m_recv_buf); + GetOwner()->CreateStream(std::bind(&HTTPReqHandler::HandleStreamRequestComplete, + shared_from_this(), std::placeholders::_1), m_ProxyURL.host, + m_ProxyURL.port); + } else { + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, + std::placeholders::_1, std::placeholders::_2, + [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind( + &HTTPReqHandler::HandleUpstreamHTTPProxyConnect, + this, std::placeholders::_1)); + })); + } + } else if (m_ProxyURL.schema == "socks") { + /* handle upstream socks proxy */ + if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, + std::placeholders::_1, std::placeholders::_2, + [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind( + &HTTPReqHandler::HandleUpstreamSocksProxyConnect, + this, std::placeholders::_1)); + })); + } else { + /* unknown type, complain */ + GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); + } + } - void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) - { - if(ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message()); - else handler(*it); - } + void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code &ec, + boost::asio::ip::tcp::resolver::iterator it, + ProxyResolvedHandler handler) { + if (ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message()); + else handler(*it); + } - void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) - { - if(!ec) { - if(m_RequestURL.host.size() > 255) { - GenericProxyError(tr("hostname too long"), m_RequestURL.host); - return; - } - uint16_t port = m_RequestURL.port; - if(!port) port = 80; - LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream"); + void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code &ec) { + if (!ec) { + if (m_RequestURL.host.size() > 255) { + GenericProxyError(tr("hostname too long"), m_RequestURL.host); + return; + } + uint16_t port = m_RequestURL.port; + if (!port) port = 80; + LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream"); - std::string host = m_RequestURL.host; - std::size_t reqsize = 0; - m_socks_buf[0] = '\x04'; - m_socks_buf[1] = 1; - htobe16buf(m_socks_buf+2, port); - m_socks_buf[4] = 0; - m_socks_buf[5] = 0; - m_socks_buf[6] = 0; - m_socks_buf[7] = 1; - // user id - m_socks_buf[8] = 'i'; - m_socks_buf[9] = '2'; - m_socks_buf[10] = 'p'; - m_socks_buf[11] = 'd'; - m_socks_buf[12] = 0; - reqsize += 13; - memcpy(m_socks_buf+ reqsize, host.c_str(), host.size()); - reqsize += host.size(); - m_socks_buf[++reqsize] = 0; - boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); - } else GenericProxyError(tr("cannot connect to upstream socks proxy"), ec.message()); - } + std::string host = m_RequestURL.host; + std::size_t reqsize = 0; + m_socks_buf[0] = '\x04'; + m_socks_buf[1] = 1; + htobe16buf(m_socks_buf + 2, port); + m_socks_buf[4] = 0; + m_socks_buf[5] = 0; + m_socks_buf[6] = 0; + m_socks_buf[7] = 1; + // user id + m_socks_buf[8] = 'i'; + m_socks_buf[9] = '2'; + m_socks_buf[10] = 'p'; + m_socks_buf[11] = 'd'; + m_socks_buf[12] = 0; + reqsize += 13; + memcpy(m_socks_buf + reqsize, host.c_str(), host.size()); + reqsize += host.size(); + m_socks_buf[++reqsize] = 0; + boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), + boost::asio::transfer_all(), + std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, + std::placeholders::_1, std::placeholders::_2)); + } else GenericProxyError(tr("cannot connect to upstream socks proxy"), ec.message()); + } - void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) - { - LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent"); - if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message()); - else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); - } + void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code &ec, + std::size_t bytes_transferred) { + LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent"); + if (ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message()); + else + m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), + std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, + std::placeholders::_1, std::placeholders::_2)); + } - void HTTPReqHandler::HandoverToUpstreamProxy() - { - LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy"); - auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); - m_sock = nullptr; - m_proxysock = nullptr; - GetOwner()->AddHandler(connection); - connection->Start(); - Terminate(); - } + void HTTPReqHandler::HandoverToUpstreamProxy() { + LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy"); + auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); + m_sock = nullptr; + m_proxysock = nullptr; + GetOwner()->AddHandler(connection); + connection->Start(); + Terminate(); + } - void HTTPReqHandler::HTTPConnect(const std::string & host, uint16_t port) - { - LogPrint(eLogDebug, "HTTPProxy: CONNECT ",host, ":", port); - std::string hostname(host); - if(str_rmatch(hostname, ".i2p")) - GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleHTTPConnectStreamRequestComplete, - shared_from_this(), std::placeholders::_1), host, port); - else - ForwardToUpstreamProxy(); - } + void HTTPReqHandler::HTTPConnect(const std::string &host, uint16_t port) { + LogPrint(eLogDebug, "HTTPProxy: CONNECT ", host, ":", port); + std::string hostname(host); + if (str_rmatch(hostname, ".i2p")) + GetOwner()->CreateStream(std::bind(&HTTPReqHandler::HandleHTTPConnectStreamRequestComplete, + shared_from_this(), std::placeholders::_1), host, port); + else + ForwardToUpstreamProxy(); + } - void HTTPReqHandler::HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream) - { - if(stream) - { - m_ClientResponse.code = 200; - m_ClientResponse.status = "OK"; - m_send_buf = m_ClientResponse.to_string(); - m_sock->send(boost::asio::buffer(m_send_buf)); - auto connection = std::make_shared(GetOwner(), m_sock, stream); - GetOwner()->AddHandler(connection); - connection->I2PConnect(); - m_sock = nullptr; - Terminate(); - } - else - { - GenericProxyError(tr("CONNECT error"), tr("Failed to Connect")); - } - } + void HTTPReqHandler::HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream) { + if (stream) { + m_ClientResponse.code = 200; + m_ClientResponse.status = "OK"; + m_send_buf = m_ClientResponse.to_string(); + m_sock->send(boost::asio::buffer(m_send_buf)); + auto connection = std::make_shared(GetOwner(), m_sock, stream); + GetOwner()->AddHandler(connection); + connection->I2PConnect(); + m_sock = nullptr; + Terminate(); + } else { + GenericProxyError(tr("CONNECT error"), tr("Failed to Connect")); + } + } - void HTTPReqHandler::SocksProxySuccess() - { - if(m_ClientRequest.method == "CONNECT") { - m_ClientResponse.code = 200; - m_send_buf = m_ClientResponse.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) GenericProxyError(tr("socks proxy error"), ec.message()); - else HandoverToUpstreamProxy(); - }); - } else { - m_send_buf = m_ClientRequestBuffer.str(); - LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes"); - boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) GenericProxyError(tr("failed to send request to upstream"), ec.message()); - else HandoverToUpstreamProxy(); - }); - } - } + void HTTPReqHandler::SocksProxySuccess() { + if (m_ClientRequest.method == "CONNECT") { + m_ClientResponse.code = 200; + m_send_buf = m_ClientResponse.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), + [&](const boost::system::error_code &ec, std::size_t transferred) { + if (ec) GenericProxyError(tr("socks proxy error"), ec.message()); + else HandoverToUpstreamProxy(); + }); + } else { + m_send_buf = m_ClientRequestBuffer.str(); + LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes"); + boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), + [&](const boost::system::error_code &ec, std::size_t transferred) { + if (ec) + GenericProxyError(tr("failed to send request to upstream"), + ec.message()); + else HandoverToUpstreamProxy(); + }); + } + } - void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred) - { - if(!ec) - { - if(m_socks_buf[1] == 90) { - // success - SocksProxySuccess(); - } else { - std::stringstream ss; - ss << "error code: "; - ss << (int) m_socks_buf[1]; - std::string msg = ss.str(); - GenericProxyError(tr("socks proxy error"), msg); - } - } - else GenericProxyError(tr("No Reply From socks proxy"), ec.message()); - } + void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code &ec, std::size_t bytes_transferred) { + if (!ec) { + if (m_socks_buf[1] == 90) { + // success + SocksProxySuccess(); + } else { + std::stringstream ss; + ss << "error code: "; + ss << (int) m_socks_buf[1]; + std::string msg = ss.str(); + GenericProxyError(tr("socks proxy error"), msg); + } + } else GenericProxyError(tr("No Reply From socks proxy"), ec.message()); + } - void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) - { - if(!ec) { - LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream"); - GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented")); - } else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message()); - } + void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code &ec) { + if (!ec) { + LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream"); + GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented")); + } else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message()); + } - /* will be called after some data received from client */ - void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) - { - LogPrint(eLogDebug, "HTTPProxy: Sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length()); - if(ecode) - { - LogPrint(eLogWarning, "HTTPProxy: Sock recv got error: ", ecode); - Terminate(); - return; - } + /* will be called after some data received from client */ + void HTTPReqHandler::HandleSockRecv(const boost::system::error_code &ecode, std::size_t len) { + LogPrint(eLogDebug, "HTTPProxy: Sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), + ", send buf: ", m_send_buf.length()); + if (ecode) { + LogPrint(eLogWarning, "HTTPProxy: Sock recv got error: ", ecode); + Terminate(); + return; + } - m_recv_buf.append(reinterpret_cast(m_recv_chunk), len); - if (HandleRequest()) { - m_recv_buf.clear(); - return; - } - AsyncSockRead(); - } + m_recv_buf.append(reinterpret_cast(m_recv_chunk), len); + if (HandleRequest()) { + m_recv_buf.clear(); + return; + } + AsyncSockRead(); + } - void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode) - { - if (ecode) - LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ()); - Terminate(); - } + void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code &ecode) { + if (ecode) + LogPrint(eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message()); + Terminate(); + } - void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (!stream) { - LogPrint (eLogError, "HTTPProxy: Error when creating the stream, check the previous warnings for more info"); - GenericProxyError(tr("Host is down"), tr("Can't create connection to requested host, it may be down. Please try again later.")); - return; - } - if (Kill()) - return; - LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID()); - auto connection = std::make_shared(GetOwner(), m_sock, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (reinterpret_cast(m_send_buf.data()), m_send_buf.length()); - Done (shared_from_this()); - } + void HTTPReqHandler::HandleStreamRequestComplete(std::shared_ptr stream) { + if (!stream) { + LogPrint(eLogError, + "HTTPProxy: Error when creating the stream, check the previous warnings for more info"); + GenericProxyError(tr("Host is down"), + tr("Can't create connection to requested host, it may be down. Please try again later.")); + return; + } + if (Kill()) + return; + LogPrint(eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", + stream->GetRecvStreamID()); + auto connection = std::make_shared(GetOwner(), m_sock, stream); + GetOwner()->AddHandler(connection); + connection->I2PConnect(reinterpret_cast(m_send_buf.data()), m_send_buf.length()); + Done(shared_from_this()); + } - HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination): - TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), - m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper) - { - } + HTTPProxy::HTTPProxy(const std::string &name, const std::string &address, int port, const std::string &outproxy, + bool addresshelper, std::shared_ptr localDestination) : + TCPIPAcceptor(address, port, + localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination()), + m_Name(name), m_OutproxyUrl(outproxy), m_Addresshelper(addresshelper) { + } - std::shared_ptr HTTPProxy::CreateHandler(std::shared_ptr socket) - { - return std::make_shared (this, socket); - } -} // http + std::shared_ptr + HTTPProxy::CreateHandler(std::shared_ptr socket) { + return std::make_shared(this, socket); + } + } // http } // i2p diff --git a/libi2pd_client/HTTPProxy.h b/libi2pd_client/HTTPProxy.h index 69ed4cef..894bcfb7 100644 --- a/libi2pd_client/HTTPProxy.h +++ b/libi2pd_client/HTTPProxy.h @@ -10,32 +10,38 @@ #define HTTP_PROXY_H__ namespace i2p { -namespace proxy { - class HTTPProxy: public i2p::client::TCPIPAcceptor - { - public: + namespace proxy { + class HTTPProxy : public i2p::client::TCPIPAcceptor { + public: - HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination); - HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr localDestination = nullptr) : - HTTPProxy(name, address, port, "", true, localDestination) {} ; - ~HTTPProxy() {}; + HTTPProxy(const std::string &name, const std::string &address, int port, const std::string &outproxy, + bool addresshelper, std::shared_ptr localDestination); - std::string GetOutproxyURL() const { return m_OutproxyUrl; } - bool GetHelperSupport() { return m_Addresshelper; } + HTTPProxy(const std::string &name, const std::string &address, int port, + std::shared_ptr localDestination = nullptr) : + HTTPProxy(name, address, port, "", true, localDestination) {}; - protected: + ~HTTPProxy() {}; - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return m_Name.c_str (); } + std::string GetOutproxyURL() const { return m_OutproxyUrl; } - private: + bool GetHelperSupport() { return m_Addresshelper; } - std::string m_Name; - std::string m_OutproxyUrl; - bool m_Addresshelper; - }; -} // http + protected: + + // Implements TCPIPAcceptor + std::shared_ptr + CreateHandler(std::shared_ptr socket); + + const char *GetName() { return m_Name.c_str(); } + + private: + + std::string m_Name; + std::string m_OutproxyUrl; + bool m_Addresshelper; + }; + } // http } // i2p #endif diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index cc0837b7..753aaaa7 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -18,1025 +18,882 @@ #include "Signature.h" #include "I2CP.h" -namespace i2p -{ -namespace client -{ +namespace i2p { + namespace client { - I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, const std::map& params): - LeaseSetDestination (service, isPublic, ¶ms), - m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()), - m_IsCreatingLeaseSet (false), m_LeaseSetCreationTimer (service) - { - } + I2CPDestination::I2CPDestination(boost::asio::io_service &service, std::shared_ptr owner, + std::shared_ptr identity, bool isPublic, + const std::map ¶ms) : + LeaseSetDestination(service, isPublic, ¶ms), + m_Owner(owner), m_Identity(identity), m_EncryptionKeyType(m_Identity->GetCryptoKeyType()), + m_IsCreatingLeaseSet(false), m_LeaseSetCreationTimer(service) { + } - void I2CPDestination::Stop () - { - LeaseSetDestination::Stop (); - m_Owner = nullptr; - m_LeaseSetCreationTimer.cancel (); - } + void I2CPDestination::Stop() { + LeaseSetDestination::Stop(); + m_Owner = nullptr; + m_LeaseSetCreationTimer.cancel(); + } - void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) - { - m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key); - } + void I2CPDestination::SetEncryptionPrivateKey(const uint8_t *key) { + m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor(m_Identity->GetCryptoKeyType(), key); + } - void I2CPDestination::SetECIESx25519EncryptionPrivateKey (const uint8_t * key) - { - if (!m_ECIESx25519Decryptor || memcmp (m_ECIESx25519PrivateKey, key, 32)) // new key? - { - m_ECIESx25519Decryptor = std::make_shared(key, true); // calculate public - memcpy (m_ECIESx25519PrivateKey, key, 32); - } - } + void I2CPDestination::SetECIESx25519EncryptionPrivateKey(const uint8_t *key) { + if (!m_ECIESx25519Decryptor || memcmp(m_ECIESx25519PrivateKey, key, 32)) // new key? + { + m_ECIESx25519Decryptor = std::make_shared(key, + true); // calculate public + memcpy(m_ECIESx25519PrivateKey, key, 32); + } + } - bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const - { - if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) - return m_ECIESx25519Decryptor->Decrypt (encrypted, data); - if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data); - else - LogPrint (eLogError, "I2CP: Decryptor is not set"); - return false; - } + bool I2CPDestination::Decrypt(const uint8_t *encrypted, uint8_t *data, + i2p::data::CryptoKeyType preferredCrypto) const { + if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) + return m_ECIESx25519Decryptor->Decrypt(encrypted, data); + if (m_Decryptor) + return m_Decryptor->Decrypt(encrypted, data); + else + LogPrint(eLogError, "I2CP: Decryptor is not set"); + return false; + } - const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const - { - if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) - return m_ECIESx25519Decryptor->GetPubicKey (); - return nullptr; - } + const uint8_t *I2CPDestination::GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const { + if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) + return m_ECIESx25519Decryptor->GetPubicKey(); + return nullptr; + } - bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const - { - return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType; - } + bool I2CPDestination::SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const { + return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool) m_ECIESx25519Decryptor : + m_EncryptionKeyType == keyType; + } - void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len) - { - uint32_t length = bufbe32toh (buf); - if (length > len - 4) length = len - 4; - if (m_Owner) - m_Owner->SendMessagePayloadMessage (buf + 4, length); - } + void I2CPDestination::HandleDataMessage(const uint8_t *buf, size_t len) { + uint32_t length = bufbe32toh(buf); + if (length > len - 4) length = len - 4; + if (m_Owner) + m_Owner->SendMessagePayloadMessage(buf + 4, length); + } - void I2CPDestination::CreateNewLeaseSet (const std::vector >& tunnels) - { - GetService ().post (std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels)); - } + void + I2CPDestination::CreateNewLeaseSet(const std::vector > &tunnels) { + GetService().post(std::bind(&I2CPDestination::PostCreateNewLeaseSet, this, tunnels)); + } - void I2CPDestination::PostCreateNewLeaseSet (std::vector > tunnels) - { - if (m_IsCreatingLeaseSet) - { - LogPrint (eLogInfo, "I2CP: LeaseSet is being created"); - return; - } - uint8_t priv[256] = {0}; - i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only - m_LeaseSetExpirationTime = ls.GetExpirationTime (); - uint8_t * leases = ls.GetLeases (); - leases[-1] = tunnels.size (); - if (m_Owner) - { - uint16_t sessionID = m_Owner->GetSessionID (); - if (sessionID != 0xFFFF) - { - m_IsCreatingLeaseSet = true; - htobe16buf (leases - 3, sessionID); - size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size (); - m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); - m_LeaseSetCreationTimer.expires_from_now (boost::posix_time::seconds (I2CP_LEASESET_CREATION_TIMEOUT)); - auto s = GetSharedFromThis (); - m_LeaseSetCreationTimer.async_wait ([s](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogInfo, "I2CP: LeaseSet creation timeout expired. Terminate"); - if (s->m_Owner) s->m_Owner->Stop (); - } - }); - } - } - } + void I2CPDestination::PostCreateNewLeaseSet(std::vector > tunnels) { + if (m_IsCreatingLeaseSet) { + LogPrint(eLogInfo, "I2CP: LeaseSet is being created"); + return; + } + uint8_t priv[256] = {0}; + i2p::data::LocalLeaseSet ls(m_Identity, priv, + tunnels); // we don't care about encryption key, we need leases only + m_LeaseSetExpirationTime = ls.GetExpirationTime(); + uint8_t *leases = ls.GetLeases(); + leases[-1] = tunnels.size(); + if (m_Owner) { + uint16_t sessionID = m_Owner->GetSessionID(); + if (sessionID != 0xFFFF) { + m_IsCreatingLeaseSet = true; + htobe16buf(leases - 3, sessionID); + size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE * tunnels.size(); + m_Owner->SendI2CPMessage(I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); + m_LeaseSetCreationTimer.expires_from_now( + boost::posix_time::seconds(I2CP_LEASESET_CREATION_TIMEOUT)); + auto s = GetSharedFromThis(); + m_LeaseSetCreationTimer.async_wait([s](const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogInfo, "I2CP: LeaseSet creation timeout expired. Terminate"); + if (s->m_Owner) s->m_Owner->Stop(); + } + }); + } + } + } - void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) - { - m_IsCreatingLeaseSet = false; - m_LeaseSetCreationTimer.cancel (); - auto ls = std::make_shared (m_Identity, buf, len); - ls->SetExpirationTime (m_LeaseSetExpirationTime); - SetLeaseSet (ls); - } + void I2CPDestination::LeaseSetCreated(const uint8_t *buf, size_t len) { + m_IsCreatingLeaseSet = false; + m_LeaseSetCreationTimer.cancel(); + auto ls = std::make_shared(m_Identity, buf, len); + ls->SetExpirationTime(m_LeaseSetExpirationTime); + SetLeaseSet(ls); + } - void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len) - { - m_IsCreatingLeaseSet = false; - m_LeaseSetCreationTimer.cancel (); - auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ? - std::make_shared (m_Identity, buf, len): - std::make_shared (storeType, m_Identity, buf, len); - ls->SetExpirationTime (m_LeaseSetExpirationTime); - SetLeaseSet (ls); - } + void I2CPDestination::LeaseSet2Created(uint8_t storeType, const uint8_t *buf, size_t len) { + m_IsCreatingLeaseSet = false; + m_LeaseSetCreationTimer.cancel(); + auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ? + std::make_shared(m_Identity, buf, len) : + std::make_shared(storeType, m_Identity, buf, len); + ls->SetExpirationTime(m_LeaseSetExpirationTime); + SetLeaseSet(ls); + } - void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce) - { - auto msg = m_I2NPMsgsPool.AcquireSharedMt (); - uint8_t * buf = msg->GetPayload (); - htobe32buf (buf, len); - memcpy (buf + 4, payload, len); - msg->len += len + 4; - msg->FillI2NPMessageHeader (eI2NPData); - auto s = GetSharedFromThis (); - auto remote = FindLeaseSet (ident); - if (remote) - { - GetService ().post ( - [s, msg, remote, nonce]() - { - bool sent = s->SendMsg (msg, remote); - if (s->m_Owner) - s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); - }); - } - else - { - RequestDestination (ident, - [s, msg, nonce](std::shared_ptr ls) - { - if (ls) - { - bool sent = s->SendMsg (msg, ls); - if (s->m_Owner) - s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); - } - else if (s->m_Owner) - s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet); - }); - } - } + void I2CPDestination::SendMsgTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident, + uint32_t nonce) { + auto msg = m_I2NPMsgsPool.AcquireSharedMt(); + uint8_t *buf = msg->GetPayload(); + htobe32buf(buf, len); + memcpy(buf + 4, payload, len); + msg->len += len + 4; + msg->FillI2NPMessageHeader(eI2NPData); + auto s = GetSharedFromThis(); + auto remote = FindLeaseSet(ident); + if (remote) { + GetService().post( + [s, msg, remote, nonce]() { + bool sent = s->SendMsg(msg, remote); + if (s->m_Owner) + s->m_Owner->SendMessageStatusMessage(nonce, sent ? eI2CPMessageStatusGuaranteedSuccess + : eI2CPMessageStatusGuaranteedFailure); + }); + } else { + RequestDestination(ident, + [s, msg, nonce](std::shared_ptr ls) { + if (ls) { + bool sent = s->SendMsg(msg, ls); + if (s->m_Owner) + s->m_Owner->SendMessageStatusMessage(nonce, sent + ? eI2CPMessageStatusGuaranteedSuccess + : eI2CPMessageStatusGuaranteedFailure); + } else if (s->m_Owner) + s->m_Owner->SendMessageStatusMessage(nonce, eI2CPMessageStatusNoLeaseSet); + }); + } + } - bool I2CPDestination::SendMsg (std::shared_ptr msg, std::shared_ptr remote) - { - auto remoteSession = GetRoutingSession (remote, true); - if (!remoteSession) - { - LogPrint (eLogError, "I2CP: Failed to create remote session"); - return false; - } - auto path = remoteSession->GetSharedRoutingPath (); - std::shared_ptr outboundTunnel; - std::shared_ptr remoteLease; - if (path) - { - if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags - { - outboundTunnel = path->outboundTunnel; - remoteLease = path->remoteLease; - } - else - remoteSession->SetSharedRoutingPath (nullptr); - } - else - { - auto leases = remote->GetNonExpiredLeases (false); // without threshold - if (leases.empty ()) - leases = remote->GetNonExpiredLeases (true); // with threshold - if (!leases.empty ()) - { - remoteLease = leases[rand () % leases.size ()]; - auto leaseRouter = i2p::data::netdb.FindRouter (remoteLease->tunnelGateway); - outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (nullptr, - leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); - } - if (remoteLease && outboundTunnel) - remoteSession->SetSharedRoutingPath (std::make_shared ( - i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT - else - remoteSession->SetSharedRoutingPath (nullptr); - } - if (remoteLease && outboundTunnel) - { - std::vector msgs; - auto garlic = remoteSession->WrapSingleMessage (msg); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeTunnel, - remoteLease->tunnelGateway, remoteLease->tunnelID, - garlic - }); - outboundTunnel->SendTunnelDataMsg (msgs); - return true; - } - else - { - if (outboundTunnel) - LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired"); - else - LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels"); - return false; - } - } + bool + I2CPDestination::SendMsg(std::shared_ptr msg, std::shared_ptr remote) { + auto remoteSession = GetRoutingSession(remote, true); + if (!remoteSession) { + LogPrint(eLogError, "I2CP: Failed to create remote session"); + return false; + } + auto path = remoteSession->GetSharedRoutingPath(); + std::shared_ptr outboundTunnel; + std::shared_ptr remoteLease; + if (path) { + if (!remoteSession->CleanupUnconfirmedTags()) // no stuck tags + { + outboundTunnel = path->outboundTunnel; + remoteLease = path->remoteLease; + } else + remoteSession->SetSharedRoutingPath(nullptr); + } else { + auto leases = remote->GetNonExpiredLeases(false); // without threshold + if (leases.empty()) + leases = remote->GetNonExpiredLeases(true); // with threshold + if (!leases.empty()) { + remoteLease = leases[rand() % leases.size()]; + auto leaseRouter = i2p::data::netdb.FindRouter(remoteLease->tunnelGateway); + outboundTunnel = GetTunnelPool()->GetNextOutboundTunnel(nullptr, + leaseRouter + ? leaseRouter->GetCompatibleTransports( + false) + : (i2p::data::RouterInfo::CompatibleTransports) i2p::data::RouterInfo::eAllTransports); + } + if (remoteLease && outboundTunnel) + remoteSession->SetSharedRoutingPath(std::make_shared( + i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT + else + remoteSession->SetSharedRoutingPath(nullptr); + } + if (remoteLease && outboundTunnel) { + std::vector msgs; + auto garlic = remoteSession->WrapSingleMessage(msg); + msgs.push_back(i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + remoteLease->tunnelGateway, remoteLease->tunnelID, + garlic + }); + outboundTunnel->SendTunnelDataMsg(msgs); + return true; + } else { + if (outboundTunnel) + LogPrint(eLogWarning, "I2CP: Failed to send message. All leases expired"); + else + LogPrint(eLogWarning, "I2CP: Failed to send message. No outbound tunnels"); + return false; + } + } - RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, const std::map& params): - RunnableService ("I2CP"), - I2CPDestination (GetIOService (), owner, identity, isPublic, params) - { - } + RunnableI2CPDestination::RunnableI2CPDestination(std::shared_ptr owner, + std::shared_ptr identity, + bool isPublic, + const std::map ¶ms) : + RunnableService("I2CP"), + I2CPDestination(GetIOService(), owner, identity, isPublic, params) { + } - RunnableI2CPDestination::~RunnableI2CPDestination () - { - if (IsRunning ()) - Stop (); - } + RunnableI2CPDestination::~RunnableI2CPDestination() { + if (IsRunning()) + Stop(); + } - void RunnableI2CPDestination::Start () - { - if (!IsRunning ()) - { - I2CPDestination::Start (); - StartIOService (); - } - } + void RunnableI2CPDestination::Start() { + if (!IsRunning()) { + I2CPDestination::Start(); + StartIOService(); + } + } - void RunnableI2CPDestination::Stop () - { - if (IsRunning ()) - { - I2CPDestination::Stop (); - StopIOService (); - } - } + void RunnableI2CPDestination::Stop() { + if (IsRunning()) { + I2CPDestination::Stop(); + StopIOService(); + } + } - I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): - m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), - m_MessageID (0), m_IsSendAccepted (true), m_IsSending (false) - { - } + I2CPSession::I2CPSession(I2CPServer &owner, std::shared_ptr socket) : + m_Owner(owner), m_Socket(socket), m_SessionID(0xFFFF), + m_MessageID(0), m_IsSendAccepted(true), m_IsSending(false) { + } - I2CPSession::~I2CPSession () - { - Terminate (); - } + I2CPSession::~I2CPSession() { + Terminate(); + } - void I2CPSession::Start () - { - ReadProtocolByte (); - } + void I2CPSession::Start() { + ReadProtocolByte(); + } - void I2CPSession::Stop () - { - Terminate (); - } + void I2CPSession::Stop() { + Terminate(); + } - void I2CPSession::ReadProtocolByte () - { - if (m_Socket) - { - auto s = shared_from_this (); - m_Socket->async_read_some (boost::asio::buffer (m_Header, 1), - [s](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE) - s->ReceiveHeader (); - else - s->Terminate (); - }); - } - } + void I2CPSession::ReadProtocolByte() { + if (m_Socket) { + auto s = shared_from_this(); + m_Socket->async_read_some(boost::asio::buffer(m_Header, 1), + [s](const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (!ecode && bytes_transferred > 0 && + s->m_Header[0] == I2CP_PROTOCOL_BYTE) + s->ReceiveHeader(); + else + s->Terminate(); + }); + } + } - void I2CPSession::ReceiveHeader () - { - if (!m_Socket) - { - LogPrint (eLogError, "I2CP: Can't receive header"); - return; - } - boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE), - boost::asio::transfer_all (), - std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void I2CPSession::ReceiveHeader() { + if (!m_Socket) { + LogPrint(eLogError, "I2CP: Can't receive header"); + return; + } + boost::asio::async_read(*m_Socket, boost::asio::buffer(m_Header, I2CP_HEADER_SIZE), + boost::asio::transfer_all(), + std::bind(&I2CPSession::HandleReceivedHeader, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void I2CPSession::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - Terminate (); - else - { - m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET); - if (m_PayloadLen > 0) - { - if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH) - ReceivePayload (); - else - { - LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen); - Terminate (); - } - } - else // no following payload - { - HandleMessage (); - ReceiveHeader (); // next message - } - } - } + void I2CPSession::HandleReceivedHeader(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) + Terminate(); + else { + m_PayloadLen = bufbe32toh(m_Header + I2CP_HEADER_LENGTH_OFFSET); + if (m_PayloadLen > 0) { + if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH) + ReceivePayload(); + else { + LogPrint(eLogError, "I2CP: Unexpected payload length ", m_PayloadLen); + Terminate(); + } + } else // no following payload + { + HandleMessage(); + ReceiveHeader(); // next message + } + } + } - void I2CPSession::ReceivePayload () - { - if (!m_Socket) - { - LogPrint (eLogError, "I2CP: Can't receive payload"); - return; - } - boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen), - boost::asio::transfer_all (), - std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void I2CPSession::ReceivePayload() { + if (!m_Socket) { + LogPrint(eLogError, "I2CP: Can't receive payload"); + return; + } + boost::asio::async_read(*m_Socket, boost::asio::buffer(m_Payload, m_PayloadLen), + boost::asio::transfer_all(), + std::bind(&I2CPSession::HandleReceivedPayload, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void I2CPSession::HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - Terminate (); - else - { - HandleMessage (); - m_PayloadLen = 0; - ReceiveHeader (); // next message - } - } + void I2CPSession::HandleReceivedPayload(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) + Terminate(); + else { + HandleMessage(); + m_PayloadLen = 0; + ReceiveHeader(); // next message + } + } - void I2CPSession::HandleMessage () - { - auto handler = m_Owner.GetMessagesHandlers ()[m_Header[I2CP_HEADER_TYPE_OFFSET]]; - if (handler) - (this->*handler)(m_Payload, m_PayloadLen); - else - LogPrint (eLogError, "I2CP: Unknown I2CP message ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); - } + void I2CPSession::HandleMessage() { + auto handler = m_Owner.GetMessagesHandlers()[m_Header[I2CP_HEADER_TYPE_OFFSET]]; + if (handler) + (this->*handler)(m_Payload, m_PayloadLen); + else + LogPrint(eLogError, "I2CP: Unknown I2CP message ", (int) m_Header[I2CP_HEADER_TYPE_OFFSET]); + } - void I2CPSession::Terminate () - { - if (m_Destination) - { - m_Destination->Stop (); - m_Destination = nullptr; - } - if (m_Socket) - { - m_Socket->close (); - m_Socket = nullptr; - } - if (!m_SendQueue.IsEmpty ()) - m_SendQueue.CleanUp (); - if (m_SessionID != 0xFFFF) - { - m_Owner.RemoveSession (GetSessionID ()); - LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " terminated"); - m_SessionID = 0xFFFF; - } - } + void I2CPSession::Terminate() { + if (m_Destination) { + m_Destination->Stop(); + m_Destination = nullptr; + } + if (m_Socket) { + m_Socket->close(); + m_Socket = nullptr; + } + if (!m_SendQueue.IsEmpty()) + m_SendQueue.CleanUp(); + if (m_SessionID != 0xFFFF) { + m_Owner.RemoveSession(GetSessionID()); + LogPrint(eLogDebug, "I2CP: Session ", m_SessionID, " terminated"); + m_SessionID = 0xFFFF; + } + } - void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) - { - auto l = len + I2CP_HEADER_SIZE; - if (l > I2CP_MAX_MESSAGE_LENGTH) - { - LogPrint (eLogError, "I2CP: Message to send is too long ", l); - return; - } - auto sendBuf = m_IsSending ? std::make_shared (l) : nullptr; - uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer; - htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len); - buf[I2CP_HEADER_TYPE_OFFSET] = type; - memcpy (buf + I2CP_HEADER_SIZE, payload, len); - if (sendBuf) - { - if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) - m_SendQueue.Add (sendBuf); - else - { - LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); - return; - } - } - else - { - auto socket = m_Socket; - if (socket) - { - m_IsSending = true; - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), - boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } - } + void I2CPSession::SendI2CPMessage(uint8_t type, const uint8_t *payload, size_t len) { + auto l = len + I2CP_HEADER_SIZE; + if (l > I2CP_MAX_MESSAGE_LENGTH) { + LogPrint(eLogError, "I2CP: Message to send is too long ", l); + return; + } + auto sendBuf = m_IsSending ? std::make_shared(l) : nullptr; + uint8_t *buf = sendBuf ? sendBuf->buf : m_SendBuffer; + htobe32buf(buf + I2CP_HEADER_LENGTH_OFFSET, len); + buf[I2CP_HEADER_TYPE_OFFSET] = type; + memcpy(buf + I2CP_HEADER_SIZE, payload, len); + if (sendBuf) { + if (m_SendQueue.GetSize() < I2CP_MAX_SEND_QUEUE_SIZE) + m_SendQueue.Add(sendBuf); + else { + LogPrint(eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); + return; + } + } else { + auto socket = m_Socket; + if (socket) { + m_IsSending = true; + boost::asio::async_write(*socket, boost::asio::buffer(m_SendBuffer, l), + boost::asio::transfer_all(), std::bind(&I2CPSession::HandleI2CPMessageSent, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + } + } + } - void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else if (!m_SendQueue.IsEmpty ()) - { - auto socket = m_Socket; - if (socket) - { - auto len = m_SendQueue.Get (m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH); - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len), - boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - m_IsSending = false; - } - else - m_IsSending = false; - } + void I2CPSession::HandleI2CPMessageSent(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else if (!m_SendQueue.IsEmpty()) { + auto socket = m_Socket; + if (socket) { + auto len = m_SendQueue.Get(m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH); + boost::asio::async_write(*socket, boost::asio::buffer(m_SendBuffer, len), + boost::asio::transfer_all(), std::bind(&I2CPSession::HandleI2CPMessageSent, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + } else + m_IsSending = false; + } else + m_IsSending = false; + } - std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len) - { - uint8_t l = buf[0]; - if (l > len) l = len; - return std::string ((const char *)(buf + 1), l); - } + std::string I2CPSession::ExtractString(const uint8_t *buf, size_t len) { + uint8_t l = buf[0]; + if (l > len) l = len; + return std::string((const char *) (buf + 1), l); + } - size_t I2CPSession::PutString (uint8_t * buf, size_t len, const std::string& str) - { - auto l = str.length (); - if (l + 1 >= len) l = len - 1; - if (l > 255) l = 255; // 1 byte max - buf[0] = l; - memcpy (buf + 1, str.c_str (), l); - return l + 1; - } + size_t I2CPSession::PutString(uint8_t *buf, size_t len, const std::string &str) { + auto l = str.length(); + if (l + 1 >= len) l = len - 1; + if (l > 255) l = 255; // 1 byte max + buf[0] = l; + memcpy(buf + 1, str.c_str(), l); + return l + 1; + } - void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) - // TODO: move to Base.cpp - { - size_t offset = 0; - while (offset < len) - { - std::string param = ExtractString (buf + offset, len - offset); - offset += param.length () + 1; - if (buf[offset] != '=') - { - LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param); - break; - } - offset++; + void I2CPSession::ExtractMapping(const uint8_t *buf, size_t len, std::map &mapping) + // TODO: move to Base.cpp + { + size_t offset = 0; + while (offset < len) { + std::string param = ExtractString(buf + offset, len - offset); + offset += param.length() + 1; + if (buf[offset] != '=') { + LogPrint(eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param); + break; + } + offset++; - std::string value = ExtractString (buf + offset, len - offset); - offset += value.length () + 1; - if (buf[offset] != ';') - { - LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value); - break; - } - offset++; - mapping.insert (std::make_pair (param, value)); - } - } + std::string value = ExtractString(buf + offset, len - offset); + offset += value.length() + 1; + if (buf[offset] != ';') { + LogPrint(eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value); + break; + } + offset++; + mapping.insert(std::make_pair(param, value)); + } + } - void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len) - { - // get version - auto version = ExtractString (buf, len); - auto l = version.length () + 1 + 8; - uint8_t * payload = new uint8_t[l]; - // set date - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - htobe64buf (payload, ts); - // echo vesrion back - PutString (payload + 8, l - 8, version); - SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload, l); - delete[] payload; - } + void I2CPSession::GetDateMessageHandler(const uint8_t *buf, size_t len) { + // get version + auto version = ExtractString(buf, len); + auto l = version.length() + 1 + 8; + uint8_t *payload = new uint8_t[l]; + // set date + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + htobe64buf(payload, ts); + // echo vesrion back + PutString(payload + 8, l - 8, version); + SendI2CPMessage(I2CP_SET_DATE_MESSAGE, payload, l); + delete[] payload; + } - void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len) - { - RAND_bytes ((uint8_t *)&m_SessionID, 2); - auto identity = std::make_shared(); - size_t offset = identity->FromBuffer (buf, len); - if (!offset) - { - LogPrint (eLogError, "I2CP: Create session malformed identity"); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - return; - } - if (m_Owner.FindSessionByIdentHash (identity->GetIdentHash ())) - { - LogPrint (eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash ().ToBase32 ()); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - return; - } - uint16_t optionsSize = bufbe16toh (buf + offset); - offset += 2; - if (optionsSize > len - offset) - { - LogPrint (eLogError, "I2CP: Options size ", optionsSize, "exceeds message size"); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - return; - } - std::map params; - ExtractMapping (buf + offset, optionsSize, params); - offset += optionsSize; // options - if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false; + void I2CPSession::CreateSessionMessageHandler(const uint8_t *buf, size_t len) { + RAND_bytes((uint8_t * ) & m_SessionID, 2); + auto identity = std::make_shared(); + size_t offset = identity->FromBuffer(buf, len); + if (!offset) { + LogPrint(eLogError, "I2CP: Create session malformed identity"); + SendSessionStatusMessage(eI2CPSessionStatusInvalid); // invalid + return; + } + if (m_Owner.FindSessionByIdentHash(identity->GetIdentHash())) { + LogPrint(eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash().ToBase32()); + SendSessionStatusMessage(eI2CPSessionStatusInvalid); // invalid + return; + } + uint16_t optionsSize = bufbe16toh(buf + offset); + offset += 2; + if (optionsSize > len - offset) { + LogPrint(eLogError, "I2CP: Options size ", optionsSize, "exceeds message size"); + SendSessionStatusMessage(eI2CPSessionStatusInvalid); // invalid + return; + } + std::map params; + ExtractMapping(buf + offset, optionsSize, params); + offset += optionsSize; // options + if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false; - offset += 8; // date - if (identity->Verify (buf, offset, buf + offset)) // signature - { - if (!m_Destination) - { - m_Destination = m_Owner.IsSingleThread () ? - std::make_shared(m_Owner.GetService (), shared_from_this (), identity, true, params): - std::make_shared(shared_from_this (), identity, true, params); - if (m_Owner.InsertSession (shared_from_this ())) - { - SendSessionStatusMessage (eI2CPSessionStatusCreated); // created - LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created"); - m_Destination->Start (); - } - else - { - LogPrint (eLogError, "I2CP: Session already exists"); - SendSessionStatusMessage (eI2CPSessionStatusRefused); - } - } - else - { - LogPrint (eLogError, "I2CP: Session already exists"); - SendSessionStatusMessage (eI2CPSessionStatusRefused); // refused - } - } - else - { - LogPrint (eLogError, "I2CP: Create session signature verification failed"); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - } - } + offset += 8; // date + if (identity->Verify(buf, offset, buf + offset)) // signature + { + if (!m_Destination) { + m_Destination = m_Owner.IsSingleThread() ? + std::make_shared(m_Owner.GetService(), shared_from_this(), + identity, true, params) : + std::make_shared(shared_from_this(), identity, true, + params); + if (m_Owner.InsertSession(shared_from_this())) { + SendSessionStatusMessage(eI2CPSessionStatusCreated); // created + LogPrint(eLogDebug, "I2CP: Session ", m_SessionID, " created"); + m_Destination->Start(); + } else { + LogPrint(eLogError, "I2CP: Session already exists"); + SendSessionStatusMessage(eI2CPSessionStatusRefused); + } + } else { + LogPrint(eLogError, "I2CP: Session already exists"); + SendSessionStatusMessage(eI2CPSessionStatusRefused); // refused + } + } else { + LogPrint(eLogError, "I2CP: Create session signature verification failed"); + SendSessionStatusMessage(eI2CPSessionStatusInvalid); // invalid + } + } - void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len) - { - SendSessionStatusMessage (eI2CPSessionStatusDestroyed); // destroy - LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " destroyed"); - Terminate (); - } + void I2CPSession::DestroySessionMessageHandler(const uint8_t *buf, size_t len) { + SendSessionStatusMessage(eI2CPSessionStatusDestroyed); // destroy + LogPrint(eLogDebug, "I2CP: Session ", m_SessionID, " destroyed"); + Terminate(); + } - void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len) - { - I2CPSessionStatus status = eI2CPSessionStatusInvalid; // rejected - if(len > sizeof(uint16_t)) - { - uint16_t sessionID = bufbe16toh(buf); - if(sessionID == m_SessionID) - { - buf += sizeof(uint16_t); - const uint8_t * body = buf; - i2p::data::IdentityEx ident; - if(ident.FromBuffer(buf, len - sizeof(uint16_t))) - { - if (ident == *m_Destination->GetIdentity()) - { - size_t identsz = ident.GetFullLen(); - buf += identsz; - uint16_t optssize = bufbe16toh(buf); - if (optssize <= len - sizeof(uint16_t) - sizeof(uint64_t) - identsz - ident.GetSignatureLen() - sizeof(uint16_t)) - { - buf += sizeof(uint16_t); - std::map opts; - ExtractMapping(buf, optssize, opts); - buf += optssize; - //uint64_t date = bufbe64toh(buf); - buf += sizeof(uint64_t); - const uint8_t * sig = buf; - if(ident.Verify(body, len - sizeof(uint16_t) - ident.GetSignatureLen(), sig)) - { - if(m_Destination->Reconfigure(opts)) - { - LogPrint(eLogInfo, "I2CP: Reconfigured destination"); - status = eI2CPSessionStatusUpdated; // updated - } - else - LogPrint(eLogWarning, "I2CP: Failed to reconfigure destination"); - } - else - LogPrint(eLogError, "I2CP: Invalid reconfigure message signature"); - } - else - LogPrint(eLogError, "I2CP: Mapping size mismatch"); - } - else - LogPrint(eLogError, "I2CP: Destination mismatch"); - } - else - LogPrint(eLogError, "I2CP: Malfromed destination"); - } - else - LogPrint(eLogError, "I2CP: Session mismatch"); - } - else - LogPrint(eLogError, "I2CP: Short message"); - SendSessionStatusMessage (status); - } + void I2CPSession::ReconfigureSessionMessageHandler(const uint8_t *buf, size_t len) { + I2CPSessionStatus status = eI2CPSessionStatusInvalid; // rejected + if (len > sizeof(uint16_t)) { + uint16_t sessionID = bufbe16toh(buf); + if (sessionID == m_SessionID) { + buf += sizeof(uint16_t); + const uint8_t *body = buf; + i2p::data::IdentityEx ident; + if (ident.FromBuffer(buf, len - sizeof(uint16_t))) { + if (ident == *m_Destination->GetIdentity()) { + size_t identsz = ident.GetFullLen(); + buf += identsz; + uint16_t optssize = bufbe16toh(buf); + if (optssize <= + len - sizeof(uint16_t) - sizeof(uint64_t) - identsz - ident.GetSignatureLen() - + sizeof(uint16_t)) { + buf += sizeof(uint16_t); + std::map opts; + ExtractMapping(buf, optssize, opts); + buf += optssize; + //uint64_t date = bufbe64toh(buf); + buf += sizeof(uint64_t); + const uint8_t *sig = buf; + if (ident.Verify(body, len - sizeof(uint16_t) - ident.GetSignatureLen(), sig)) { + if (m_Destination->Reconfigure(opts)) { + LogPrint(eLogInfo, "I2CP: Reconfigured destination"); + status = eI2CPSessionStatusUpdated; // updated + } else + LogPrint(eLogWarning, "I2CP: Failed to reconfigure destination"); + } else + LogPrint(eLogError, "I2CP: Invalid reconfigure message signature"); + } else + LogPrint(eLogError, "I2CP: Mapping size mismatch"); + } else + LogPrint(eLogError, "I2CP: Destination mismatch"); + } else + LogPrint(eLogError, "I2CP: Malfromed destination"); + } else + LogPrint(eLogError, "I2CP: Session mismatch"); + } else + LogPrint(eLogError, "I2CP: Short message"); + SendSessionStatusMessage(status); + } - void I2CPSession::SendSessionStatusMessage (I2CPSessionStatus status) - { - uint8_t buf[3]; - htobe16buf (buf, m_SessionID); - buf[2] = (uint8_t)status; - SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3); - } + void I2CPSession::SendSessionStatusMessage(I2CPSessionStatus status) { + uint8_t buf[3]; + htobe16buf(buf, m_SessionID); + buf[2] = (uint8_t) status; + SendI2CPMessage(I2CP_SESSION_STATUS_MESSAGE, buf, 3); + } - void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status) - { - if (!nonce) return; // don't send status with zero nonce - uint8_t buf[15]; - htobe16buf (buf, m_SessionID); - htobe32buf (buf + 2, m_MessageID++); - buf[6] = (uint8_t)status; - memset (buf + 7, 0, 4); // size - htobe32buf (buf + 11, nonce); - SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15); - } + void I2CPSession::SendMessageStatusMessage(uint32_t nonce, I2CPMessageStatus status) { + if (!nonce) return; // don't send status with zero nonce + uint8_t buf[15]; + htobe16buf(buf, m_SessionID); + htobe32buf(buf + 2, m_MessageID++); + buf[6] = (uint8_t) status; + memset(buf + 7, 0, 4); // size + htobe32buf(buf + 11, nonce); + SendI2CPMessage(I2CP_MESSAGE_STATUS_MESSAGE, buf, 15); + } - void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID) - { - size_t offset = 2; - if (m_Destination) - { - offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key - // we always assume this field as 20 bytes (DSA) regardless actual size - // instead of - //offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); - m_Destination->SetEncryptionPrivateKey (buf + offset); - offset += 256; - m_Destination->LeaseSetCreated (buf + offset, len - offset); - } - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } + void I2CPSession::CreateLeaseSetMessageHandler(const uint8_t *buf, size_t len) { + uint16_t sessionID = bufbe16toh(buf); + if (sessionID == m_SessionID) { + size_t offset = 2; + if (m_Destination) { + offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key + // we always assume this field as 20 bytes (DSA) regardless actual size + // instead of + //offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); + m_Destination->SetEncryptionPrivateKey(buf + offset); + offset += 256; + m_Destination->LeaseSetCreated(buf + offset, len - offset); + } + } else + LogPrint(eLogError, "I2CP: Unexpected sessionID ", sessionID); + } - void I2CPSession::CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID) - { - size_t offset = 2; - if (m_Destination) - { - uint8_t storeType = buf[offset]; offset++; // store type - i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted - if (!ls.IsValid ()) - { - LogPrint (eLogError, "I2CP: Invalid LeaseSet2 of type ", storeType); - return; - } - offset += ls.GetBufferLen (); - // private keys - int numPrivateKeys = buf[offset]; offset++; - for (int i = 0; i < numPrivateKeys; i++) - { - if (offset + 4 > len) return; - uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type - uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length - if (offset + keyLen > len) return; - if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset); - else - { - m_Destination->SetEncryptionType (keyType); - m_Destination->SetEncryptionPrivateKey (buf + offset); - } - offset += keyLen; - } + void I2CPSession::CreateLeaseSet2MessageHandler(const uint8_t *buf, size_t len) { + uint16_t sessionID = bufbe16toh(buf); + if (sessionID == m_SessionID) { + size_t offset = 2; + if (m_Destination) { + uint8_t storeType = buf[offset]; + offset++; // store type + i2p::data::LeaseSet2 ls(storeType, buf + offset, len - offset); // outer layer only for encrypted + if (!ls.IsValid()) { + LogPrint(eLogError, "I2CP: Invalid LeaseSet2 of type ", storeType); + return; + } + offset += ls.GetBufferLen(); + // private keys + int numPrivateKeys = buf[offset]; + offset++; + for (int i = 0; i < numPrivateKeys; i++) { + if (offset + 4 > len) return; + uint16_t keyType = bufbe16toh(buf + offset); + offset += 2; // encryption type + uint16_t keyLen = bufbe16toh(buf + offset); + offset += 2; // private key length + if (offset + keyLen > len) return; + if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + m_Destination->SetECIESx25519EncryptionPrivateKey(buf + offset); + else { + m_Destination->SetEncryptionType(keyType); + m_Destination->SetEncryptionPrivateKey(buf + offset); + } + offset += keyLen; + } - m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); - } - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } + m_Destination->LeaseSet2Created(storeType, ls.GetBuffer(), ls.GetBufferLen()); + } + } else + LogPrint(eLogError, "I2CP: Unexpected sessionID ", sessionID); + } - void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID) - { - size_t offset = 2; - if (m_Destination) - { - i2p::data::IdentityEx identity; - size_t identsize = identity.FromBuffer (buf + offset, len - offset); - if (identsize) - { - offset += identsize; - uint32_t payloadLen = bufbe32toh (buf + offset); - if (payloadLen + offset <= len) - { - offset += 4; - uint32_t nonce = bufbe32toh (buf + offset + payloadLen); - if (m_IsSendAccepted) - SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted - m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce); - } - else - LogPrint(eLogError, "I2CP: Cannot send message, too big"); - } - else - LogPrint(eLogError, "I2CP: Invalid identity"); - } - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } + void I2CPSession::SendMessageMessageHandler(const uint8_t *buf, size_t len) { + uint16_t sessionID = bufbe16toh(buf); + if (sessionID == m_SessionID) { + size_t offset = 2; + if (m_Destination) { + i2p::data::IdentityEx identity; + size_t identsize = identity.FromBuffer(buf + offset, len - offset); + if (identsize) { + offset += identsize; + uint32_t payloadLen = bufbe32toh(buf + offset); + if (payloadLen + offset <= len) { + offset += 4; + uint32_t nonce = bufbe32toh(buf + offset + payloadLen); + if (m_IsSendAccepted) + SendMessageStatusMessage(nonce, eI2CPMessageStatusAccepted); // accepted + m_Destination->SendMsgTo(buf + offset, payloadLen, identity.GetIdentHash(), nonce); + } else + LogPrint(eLogError, "I2CP: Cannot send message, too big"); + } else + LogPrint(eLogError, "I2CP: Invalid identity"); + } + } else + LogPrint(eLogError, "I2CP: Unexpected sessionID ", sessionID); + } - void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len) - { - SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6) - } + void I2CPSession::SendMessageExpiresMessageHandler(const uint8_t *buf, size_t len) { + SendMessageMessageHandler(buf, len - 8); // ignore flags(2) and expiration(6) + } - void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID || sessionID == 0xFFFF) // -1 means without session - { - uint32_t requestID = bufbe32toh (buf + 2); - //uint32_t timeout = bufbe32toh (buf + 6); - i2p::data::IdentHash ident; - switch (buf[10]) - { - case 0: // hash - ident = i2p::data::IdentHash (buf + 11); - break; - case 1: // address - { - auto name = ExtractString (buf + 11, len - 11); - auto addr = i2p::client::context.GetAddressBook ().GetAddress (name); - if (!addr || !addr->IsIdentHash ()) - { - // TODO: handle blinded addresses - LogPrint (eLogError, "I2CP: Address ", name, " not found"); - SendHostReplyMessage (requestID, nullptr); - return; - } - else - ident = addr->identHash; - break; - } - default: - LogPrint (eLogError, "I2CP: Request type ", (int)buf[10], " is not supported"); - SendHostReplyMessage (requestID, nullptr); - return; - } + void I2CPSession::HostLookupMessageHandler(const uint8_t *buf, size_t len) { + uint16_t sessionID = bufbe16toh(buf); + if (sessionID == m_SessionID || sessionID == 0xFFFF) // -1 means without session + { + uint32_t requestID = bufbe32toh(buf + 2); + //uint32_t timeout = bufbe32toh (buf + 6); + i2p::data::IdentHash ident; + switch (buf[10]) { + case 0: // hash + ident = i2p::data::IdentHash(buf + 11); + break; + case 1: // address + { + auto name = ExtractString(buf + 11, len - 11); + auto addr = i2p::client::context.GetAddressBook().GetAddress(name); + if (!addr || !addr->IsIdentHash()) { + // TODO: handle blinded addresses + LogPrint(eLogError, "I2CP: Address ", name, " not found"); + SendHostReplyMessage(requestID, nullptr); + return; + } else + ident = addr->identHash; + break; + } + default: + LogPrint(eLogError, "I2CP: Request type ", (int) buf[10], " is not supported"); + SendHostReplyMessage(requestID, nullptr); + return; + } - std::shared_ptr destination = m_Destination; - if(!destination) destination = i2p::client::context.GetSharedLocalDestination (); - if (destination) - { - auto ls = destination->FindLeaseSet (ident); - if (ls) - SendHostReplyMessage (requestID, ls->GetIdentity ()); - else - { - auto s = shared_from_this (); - destination->RequestDestination (ident, - [s, requestID](std::shared_ptr leaseSet) - { - s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr); - }); - } - } - else - SendHostReplyMessage (requestID, nullptr); - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } + std::shared_ptr destination = m_Destination; + if (!destination) destination = i2p::client::context.GetSharedLocalDestination(); + if (destination) { + auto ls = destination->FindLeaseSet(ident); + if (ls) + SendHostReplyMessage(requestID, ls->GetIdentity()); + else { + auto s = shared_from_this(); + destination->RequestDestination(ident, + [s, requestID](std::shared_ptr leaseSet) { + s->SendHostReplyMessage(requestID, + leaseSet ? leaseSet->GetIdentity() + : nullptr); + }); + } + } else + SendHostReplyMessage(requestID, nullptr); + } else + LogPrint(eLogError, "I2CP: Unexpected sessionID ", sessionID); + } - void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity) - { - if (identity) - { - size_t l = identity->GetFullLen () + 7; - uint8_t * buf = new uint8_t[l]; - htobe16buf (buf, m_SessionID); - htobe32buf (buf + 2, requestID); - buf[6] = 0; // result code - identity->ToBuffer (buf + 7, l - 7); - SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l); - delete[] buf; - } - else - { - uint8_t buf[7]; - htobe16buf (buf, m_SessionID); - htobe32buf (buf + 2, requestID); - buf[6] = 1; // result code - SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7); - } - } + void + I2CPSession::SendHostReplyMessage(uint32_t requestID, std::shared_ptr identity) { + if (identity) { + size_t l = identity->GetFullLen() + 7; + uint8_t *buf = new uint8_t[l]; + htobe16buf(buf, m_SessionID); + htobe32buf(buf + 2, requestID); + buf[6] = 0; // result code + identity->ToBuffer(buf + 7, l - 7); + SendI2CPMessage(I2CP_HOST_REPLY_MESSAGE, buf, l); + delete[] buf; + } else { + uint8_t buf[7]; + htobe16buf(buf, m_SessionID); + htobe32buf(buf + 2, requestID); + buf[6] = 1; // result code + SendI2CPMessage(I2CP_HOST_REPLY_MESSAGE, buf, 7); + } + } - void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len) - { - if (m_Destination) - { - auto ls = m_Destination->FindLeaseSet (buf); - if (ls) - { - auto l = ls->GetIdentity ()->GetFullLen (); - uint8_t * identBuf = new uint8_t[l]; - ls->GetIdentity ()->ToBuffer (identBuf, l); - SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l); - delete[] identBuf; - } - else - { - auto s = shared_from_this (); - i2p::data::IdentHash ident (buf); - m_Destination->RequestDestination (ident, - [s, ident](std::shared_ptr leaseSet) - { - if (leaseSet) // found - { - auto l = leaseSet->GetIdentity ()->GetFullLen (); - uint8_t * identBuf = new uint8_t[l]; - leaseSet->GetIdentity ()->ToBuffer (identBuf, l); - s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l); - delete[] identBuf; - } - else - s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found - }); - } - } - else - SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32); - } + void I2CPSession::DestLookupMessageHandler(const uint8_t *buf, size_t len) { + if (m_Destination) { + auto ls = m_Destination->FindLeaseSet(buf); + if (ls) { + auto l = ls->GetIdentity()->GetFullLen(); + uint8_t *identBuf = new uint8_t[l]; + ls->GetIdentity()->ToBuffer(identBuf, l); + SendI2CPMessage(I2CP_DEST_REPLY_MESSAGE, identBuf, l); + delete[] identBuf; + } else { + auto s = shared_from_this(); + i2p::data::IdentHash ident(buf); + m_Destination->RequestDestination(ident, + [s, ident](std::shared_ptr leaseSet) { + if (leaseSet) // found + { + auto l = leaseSet->GetIdentity()->GetFullLen(); + uint8_t *identBuf = new uint8_t[l]; + leaseSet->GetIdentity()->ToBuffer(identBuf, l); + s->SendI2CPMessage(I2CP_DEST_REPLY_MESSAGE, identBuf, l); + delete[] identBuf; + } else + s->SendI2CPMessage(I2CP_DEST_REPLY_MESSAGE, ident, + 32); // not found + }); + } + } else + SendI2CPMessage(I2CP_DEST_REPLY_MESSAGE, buf, 32); + } - void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len) - { - uint8_t limits[64]; - memset (limits, 0, 64); - htobe32buf (limits, i2p::transport::transports.GetInBandwidth ()); // inbound - htobe32buf (limits + 4, i2p::transport::transports.GetOutBandwidth ()); // outbound - SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64); - } + void I2CPSession::GetBandwidthLimitsMessageHandler(const uint8_t *buf, size_t len) { + uint8_t limits[64]; + memset(limits, 0, 64); + htobe32buf(limits, i2p::transport::transports.GetInBandwidth()); // inbound + htobe32buf(limits + 4, i2p::transport::transports.GetOutBandwidth()); // outbound + SendI2CPMessage(I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64); + } - void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len) - { - // we don't use SendI2CPMessage to eliminate additional copy - auto l = len + 10 + I2CP_HEADER_SIZE; - if (l > I2CP_MAX_MESSAGE_LENGTH) - { - LogPrint (eLogError, "I2CP: Message to send is too long ", l); - return; - } - auto sendBuf = m_IsSending ? std::make_shared (l) : nullptr; - uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer; - htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10); - buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE; - htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID); - htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++); - htobe32buf (buf + I2CP_HEADER_SIZE + 6, len); - memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len); - if (sendBuf) - { - if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) - m_SendQueue.Add (sendBuf); - else - { - LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); - return; - } - } - else - { - auto socket = m_Socket; - if (socket) - { - m_IsSending = true; - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), - boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } - } + void I2CPSession::SendMessagePayloadMessage(const uint8_t *payload, size_t len) { + // we don't use SendI2CPMessage to eliminate additional copy + auto l = len + 10 + I2CP_HEADER_SIZE; + if (l > I2CP_MAX_MESSAGE_LENGTH) { + LogPrint(eLogError, "I2CP: Message to send is too long ", l); + return; + } + auto sendBuf = m_IsSending ? std::make_shared(l) : nullptr; + uint8_t *buf = sendBuf ? sendBuf->buf : m_SendBuffer; + htobe32buf(buf + I2CP_HEADER_LENGTH_OFFSET, len + 10); + buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE; + htobe16buf(buf + I2CP_HEADER_SIZE, m_SessionID); + htobe32buf(buf + I2CP_HEADER_SIZE + 2, m_MessageID++); + htobe32buf(buf + I2CP_HEADER_SIZE + 6, len); + memcpy(buf + I2CP_HEADER_SIZE + 10, payload, len); + if (sendBuf) { + if (m_SendQueue.GetSize() < I2CP_MAX_SEND_QUEUE_SIZE) + m_SendQueue.Add(sendBuf); + else { + LogPrint(eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); + return; + } + } else { + auto socket = m_Socket; + if (socket) { + m_IsSending = true; + boost::asio::async_write(*socket, boost::asio::buffer(m_SendBuffer, l), + boost::asio::transfer_all(), std::bind(&I2CPSession::HandleI2CPMessageSent, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + } + } + } - I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread): - RunnableService ("I2CP"), m_IsSingleThread (isSingleThread), - m_Acceptor (GetIOService (), - boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) - { - memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); - m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; - m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler; - m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler; - m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler; - m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; - m_MessagesHandlers[I2CP_CREATE_LEASESET2_MESSAGE] = &I2CPSession::CreateLeaseSet2MessageHandler; - m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; - m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler; - m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler; - m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler; - m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler; - } + I2CPServer::I2CPServer(const std::string &interface, int port, bool isSingleThread) : + RunnableService("I2CP"), m_IsSingleThread(isSingleThread), + m_Acceptor(GetIOService(), + boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) { + memset(m_MessagesHandlers, 0, sizeof(m_MessagesHandlers)); + m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; + m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler; + m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler; + m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler; + m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; + m_MessagesHandlers[I2CP_CREATE_LEASESET2_MESSAGE] = &I2CPSession::CreateLeaseSet2MessageHandler; + m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; + m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler; + m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler; + m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler; + m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler; + } - I2CPServer::~I2CPServer () - { - if (IsRunning ()) - Stop (); - } + I2CPServer::~I2CPServer() { + if (IsRunning()) + Stop(); + } - void I2CPServer::Start () - { - Accept (); - StartIOService (); - } + void I2CPServer::Start() { + Accept(); + StartIOService(); + } - void I2CPServer::Stop () - { - m_Acceptor.cancel (); - { - auto sessions = m_Sessions; - for (auto& it: sessions) - it.second->Stop (); - } - m_Sessions.clear (); - StopIOService (); - } + void I2CPServer::Stop() { + m_Acceptor.cancel(); + { + auto sessions = m_Sessions; + for (auto &it: sessions) + it.second->Stop(); + } + m_Sessions.clear(); + StopIOService(); + } - void I2CPServer::Accept () - { - auto newSocket = std::make_shared (GetIOService ()); - m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this, - std::placeholders::_1, newSocket)); - } + void I2CPServer::Accept() { + auto newSocket = std::make_shared(GetIOService()); + m_Acceptor.async_accept(*newSocket, std::bind(&I2CPServer::HandleAccept, this, + std::placeholders::_1, newSocket)); + } - void I2CPServer::HandleAccept(const boost::system::error_code& ecode, - std::shared_ptr socket) - { - if (!ecode && socket) - { - boost::system::error_code ec; - auto ep = socket->remote_endpoint (ec); - if (!ec) - { - LogPrint (eLogDebug, "I2CP: New connection from ", ep); - auto session = std::make_shared(*this, socket); - session->Start (); - } - else - LogPrint (eLogError, "I2CP: Incoming connection error ", ec.message ()); - } - else - LogPrint (eLogError, "I2CP: Accept error: ", ecode.message ()); + void I2CPServer::HandleAccept(const boost::system::error_code &ecode, + std::shared_ptr socket) { + if (!ecode && socket) { + boost::system::error_code ec; + auto ep = socket->remote_endpoint(ec); + if (!ec) { + LogPrint(eLogDebug, "I2CP: New connection from ", ep); + auto session = std::make_shared(*this, socket); + session->Start(); + } else + LogPrint(eLogError, "I2CP: Incoming connection error ", ec.message()); + } else + LogPrint(eLogError, "I2CP: Accept error: ", ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Accept (); - } + if (ecode != boost::asio::error::operation_aborted) + Accept(); + } - bool I2CPServer::InsertSession (std::shared_ptr session) - { - if (!session) return false; - if (!m_Sessions.insert({session->GetSessionID (), session}).second) - { - LogPrint (eLogError, "I2CP: Duplicate session id ", session->GetSessionID ()); - return false; - } - return true; - } + bool I2CPServer::InsertSession(std::shared_ptr session) { + if (!session) return false; + if (!m_Sessions.insert({session->GetSessionID(), session}).second) { + LogPrint(eLogError, "I2CP: Duplicate session id ", session->GetSessionID()); + return false; + } + return true; + } - void I2CPServer::RemoveSession (uint16_t sessionID) - { - m_Sessions.erase (sessionID); - } + void I2CPServer::RemoveSession(uint16_t sessionID) { + m_Sessions.erase(sessionID); + } - std::shared_ptr I2CPServer::FindSessionByIdentHash (const i2p::data::IdentHash& ident) const - { - for (const auto& it: m_Sessions) - { - if (it.second) - { - auto dest = it.second->GetDestination (); - if (dest && dest->GetIdentHash () == ident) - return it.second; - } - } - return nullptr; - } -} + std::shared_ptr I2CPServer::FindSessionByIdentHash(const i2p::data::IdentHash &ident) const { + for (const auto &it: m_Sessions) { + if (it.second) { + auto dest = it.second->GetDestination(); + if (dest && dest->GetIdentHash() == ident) + return it.second; + } + } + return nullptr; + } + } } diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index e38da0ac..c6c8f6a1 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -19,233 +19,281 @@ #include "Destination.h" #include "Streaming.h" -namespace i2p -{ -namespace client -{ - const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; - const size_t I2CP_SESSION_BUFFER_SIZE = 4096; - const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; - const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M - const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds +namespace i2p { + namespace client { + const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; + const size_t I2CP_SESSION_BUFFER_SIZE = 4096; + const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; + const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024 * 1024; // in bytes, 1M + const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds - const size_t I2CP_HEADER_LENGTH_OFFSET = 0; - const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; - const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; + const size_t I2CP_HEADER_LENGTH_OFFSET = 0; + const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; + const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; - const uint8_t I2CP_GET_DATE_MESSAGE = 32; - const uint8_t I2CP_SET_DATE_MESSAGE = 33; - const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1; - const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2; - const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20; - const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; - const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; - const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4; - const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 41; - const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5; - const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36; - const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; - const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22; - const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38; - const uint8_t I2CP_HOST_REPLY_MESSAGE = 39; - const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34; - const uint8_t I2CP_DEST_REPLY_MESSAGE = 35; - const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8; - const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23; + const uint8_t I2CP_GET_DATE_MESSAGE = 32; + const uint8_t I2CP_SET_DATE_MESSAGE = 33; + const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1; + const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2; + const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20; + const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; + const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; + const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4; + const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 41; + const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5; + const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36; + const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; + const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22; + const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38; + const uint8_t I2CP_HOST_REPLY_MESSAGE = 39; + const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34; + const uint8_t I2CP_DEST_REPLY_MESSAGE = 35; + const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8; + const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23; - enum I2CPMessageStatus - { - eI2CPMessageStatusAccepted = 1, - eI2CPMessageStatusGuaranteedSuccess = 4, - eI2CPMessageStatusGuaranteedFailure = 5, - eI2CPMessageStatusNoLeaseSet = 21 - }; + enum I2CPMessageStatus { + eI2CPMessageStatusAccepted = 1, + eI2CPMessageStatusGuaranteedSuccess = 4, + eI2CPMessageStatusGuaranteedFailure = 5, + eI2CPMessageStatusNoLeaseSet = 21 + }; - enum I2CPSessionStatus - { - eI2CPSessionStatusDestroyed = 0, - eI2CPSessionStatusCreated = 1, - eI2CPSessionStatusUpdated = 2, - eI2CPSessionStatusInvalid = 3, - eI2CPSessionStatusRefused = 4 - }; + enum I2CPSessionStatus { + eI2CPSessionStatusDestroyed = 0, + eI2CPSessionStatusCreated = 1, + eI2CPSessionStatusUpdated = 2, + eI2CPSessionStatusInvalid = 3, + eI2CPSessionStatusRefused = 4 + }; - // params - const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; + // params + const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; - class I2CPSession; - class I2CPDestination: public LeaseSetDestination - { - public: + class I2CPSession; - I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, const std::map& params); - ~I2CPDestination () {}; + class I2CPDestination : public LeaseSetDestination { + public: - void Stop (); + I2CPDestination(boost::asio::io_service &service, std::shared_ptr owner, + std::shared_ptr identity, bool isPublic, + const std::map ¶ms); - void SetEncryptionPrivateKey (const uint8_t * key); - void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; - void SetECIESx25519EncryptionPrivateKey (const uint8_t * key); - void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession - void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession - void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession + ~I2CPDestination() {}; - // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; - const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only - std::shared_ptr GetIdentity () const { return m_Identity; }; + void Stop(); - protected: + void SetEncryptionPrivateKey(const uint8_t *key); - // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len); - void CreateNewLeaseSet (const std::vector >& tunnels); + void SetEncryptionType(i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; - private: + void SetECIESx25519EncryptionPrivateKey(const uint8_t *key); - std::shared_ptr GetSharedFromThis () - { return std::static_pointer_cast(shared_from_this ()); } - bool SendMsg (std::shared_ptr msg, std::shared_ptr remote); + void LeaseSetCreated(const uint8_t *buf, size_t len); // called from I2CPSession + void LeaseSet2Created(uint8_t storeType, const uint8_t *buf, size_t len); // called from I2CPSession + void SendMsgTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident, + uint32_t nonce); // called from I2CPSession - void PostCreateNewLeaseSet (std::vector > tunnels); + // implements LocalDestination + bool Decrypt(const uint8_t *encrypted, uint8_t *data, i2p::data::CryptoKeyType preferredCrypto) const; - private: + bool SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const; - std::shared_ptr m_Owner; - std::shared_ptr m_Identity; - i2p::data::CryptoKeyType m_EncryptionKeyType; - std::shared_ptr m_Decryptor; // standard - std::shared_ptr m_ECIESx25519Decryptor; - uint8_t m_ECIESx25519PrivateKey[32]; - uint64_t m_LeaseSetExpirationTime; - bool m_IsCreatingLeaseSet; - boost::asio::deadline_timer m_LeaseSetCreationTimer; - i2p::util::MemoryPoolMt > m_I2NPMsgsPool; - }; + const uint8_t *GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const; // for 4 only + std::shared_ptr GetIdentity() const { return m_Identity; }; - class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination - { - public: + protected: - RunnableI2CPDestination (std::shared_ptr owner, std::shared_ptr identity, - bool isPublic, const std::map& params); - ~RunnableI2CPDestination (); + // I2CP + void HandleDataMessage(const uint8_t *buf, size_t len); - void Start (); - void Stop (); - }; + void CreateNewLeaseSet(const std::vector > &tunnels); - class I2CPServer; - class I2CPSession: public std::enable_shared_from_this - { - public: + private: - I2CPSession (I2CPServer& owner, std::shared_ptr socket); + std::shared_ptr GetSharedFromThis() { + return std::static_pointer_cast(shared_from_this()); + } - ~I2CPSession (); + bool SendMsg(std::shared_ptr msg, std::shared_ptr remote); - void Start (); - void Stop (); - uint16_t GetSessionID () const { return m_SessionID; }; - std::shared_ptr GetDestination () const { return m_Destination; }; + void PostCreateNewLeaseSet(std::vector > tunnels); - // called from I2CPDestination - void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len); - void SendMessagePayloadMessage (const uint8_t * payload, size_t len); - void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status); + private: - // message handlers - void GetDateMessageHandler (const uint8_t * buf, size_t len); - void CreateSessionMessageHandler (const uint8_t * buf, size_t len); - void DestroySessionMessageHandler (const uint8_t * buf, size_t len); - void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len); - void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len); - void CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len); - void SendMessageMessageHandler (const uint8_t * buf, size_t len); - void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len); - void HostLookupMessageHandler (const uint8_t * buf, size_t len); - void DestLookupMessageHandler (const uint8_t * buf, size_t len); - void GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len); + std::shared_ptr m_Owner; + std::shared_ptr m_Identity; + i2p::data::CryptoKeyType m_EncryptionKeyType; + std::shared_ptr m_Decryptor; // standard + std::shared_ptr m_ECIESx25519Decryptor; + uint8_t m_ECIESx25519PrivateKey[32]; + uint64_t m_LeaseSetExpirationTime; + bool m_IsCreatingLeaseSet; + boost::asio::deadline_timer m_LeaseSetCreationTimer; + i2p::util::MemoryPoolMt > + m_I2NPMsgsPool; + }; - private: + class RunnableI2CPDestination : private i2p::util::RunnableService, public I2CPDestination { + public: - void ReadProtocolByte (); - void ReceiveHeader (); - void HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void ReceivePayload (); - void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleMessage (); - void Terminate (); + RunnableI2CPDestination(std::shared_ptr owner, + std::shared_ptr identity, + bool isPublic, const std::map ¶ms); - void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + ~RunnableI2CPDestination(); - std::string ExtractString (const uint8_t * buf, size_t len); - size_t PutString (uint8_t * buf, size_t len, const std::string& str); - void ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping); - void SendSessionStatusMessage (I2CPSessionStatus status); - void SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity); + void Start(); - private: + void Stop(); + }; - I2CPServer& m_Owner; - std::shared_ptr m_Socket; - uint8_t m_Header[I2CP_HEADER_SIZE], m_Payload[I2CP_MAX_MESSAGE_LENGTH]; - size_t m_PayloadLen; + class I2CPServer; - std::shared_ptr m_Destination; - uint16_t m_SessionID; - uint32_t m_MessageID; - bool m_IsSendAccepted; + class I2CPSession : public std::enable_shared_from_this { + public: - // to client - bool m_IsSending; - uint8_t m_SendBuffer[I2CP_MAX_MESSAGE_LENGTH]; - i2p::stream::SendBufferQueue m_SendQueue; - }; - typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); + I2CPSession(I2CPServer &owner, std::shared_ptr socket); - class I2CPServer: private i2p::util::RunnableService - { - public: + ~I2CPSession(); - I2CPServer (const std::string& interface, int port, bool isSingleThread); - ~I2CPServer (); + void Start(); - void Start (); - void Stop (); - boost::asio::io_service& GetService () { return GetIOService (); }; - bool IsSingleThread () const { return m_IsSingleThread; }; + void Stop(); - bool InsertSession (std::shared_ptr session); - void RemoveSession (uint16_t sessionID); - std::shared_ptr FindSessionByIdentHash (const i2p::data::IdentHash& ident) const; + uint16_t GetSessionID() const { return m_SessionID; }; - private: + std::shared_ptr GetDestination() const { return m_Destination; }; - void Run (); + // called from I2CPDestination + void SendI2CPMessage(uint8_t type, const uint8_t *payload, size_t len); - void Accept (); + void SendMessagePayloadMessage(const uint8_t *payload, size_t len); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void SendMessageStatusMessage(uint32_t nonce, I2CPMessageStatus status); - private: + // message handlers + void GetDateMessageHandler(const uint8_t *buf, size_t len); - bool m_IsSingleThread; - I2CPMessageHandler m_MessagesHandlers[256]; - std::map > m_Sessions; + void CreateSessionMessageHandler(const uint8_t *buf, size_t len); - boost::asio::ip::tcp::acceptor m_Acceptor; + void DestroySessionMessageHandler(const uint8_t *buf, size_t len); - public: + void ReconfigureSessionMessageHandler(const uint8_t *buf, size_t len); - const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; }; + void CreateLeaseSetMessageHandler(const uint8_t *buf, size_t len); - // for HTTP - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; -} + void CreateLeaseSet2MessageHandler(const uint8_t *buf, size_t len); + + void SendMessageMessageHandler(const uint8_t *buf, size_t len); + + void SendMessageExpiresMessageHandler(const uint8_t *buf, size_t len); + + void HostLookupMessageHandler(const uint8_t *buf, size_t len); + + void DestLookupMessageHandler(const uint8_t *buf, size_t len); + + void GetBandwidthLimitsMessageHandler(const uint8_t *buf, size_t len); + + private: + + void ReadProtocolByte(); + + void ReceiveHeader(); + + void HandleReceivedHeader(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void ReceivePayload(); + + void HandleReceivedPayload(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void HandleMessage(); + + void Terminate(); + + void HandleI2CPMessageSent(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + std::string ExtractString(const uint8_t *buf, size_t len); + + size_t PutString(uint8_t *buf, size_t len, const std::string &str); + + void ExtractMapping(const uint8_t *buf, size_t len, std::map &mapping); + + void SendSessionStatusMessage(I2CPSessionStatus status); + + void SendHostReplyMessage(uint32_t requestID, std::shared_ptr identity); + + private: + + I2CPServer &m_Owner; + std::shared_ptr m_Socket; + uint8_t m_Header[I2CP_HEADER_SIZE], m_Payload[I2CP_MAX_MESSAGE_LENGTH]; + size_t m_PayloadLen; + + std::shared_ptr m_Destination; + uint16_t m_SessionID; + uint32_t m_MessageID; + bool m_IsSendAccepted; + + // to client + bool m_IsSending; + uint8_t m_SendBuffer[I2CP_MAX_MESSAGE_LENGTH]; + i2p::stream::SendBufferQueue m_SendQueue; + }; + + typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t *buf, size_t len); + + class I2CPServer : private i2p::util::RunnableService { + public: + + I2CPServer(const std::string &interface, int port, bool isSingleThread); + + ~I2CPServer(); + + void Start(); + + void Stop(); + + boost::asio::io_service &GetService() { return GetIOService(); }; + + bool IsSingleThread() const { return m_IsSingleThread; }; + + bool InsertSession(std::shared_ptr session); + + void RemoveSession(uint16_t sessionID); + + std::shared_ptr FindSessionByIdentHash(const i2p::data::IdentHash &ident) const; + + private: + + void Run(); + + void Accept(); + + void + HandleAccept(const boost::system::error_code &ecode, std::shared_ptr socket); + + private: + + bool m_IsSingleThread; + I2CPMessageHandler m_MessagesHandlers[256]; + std::map > m_Sessions; + + boost::asio::ip::tcp::acceptor m_Acceptor; + + public: + + const decltype(m_MessagesHandlers) + & + + GetMessagesHandlers() const { return m_MessagesHandlers; }; + + // for HTTP + const decltype(m_Sessions) + & + + GetSessions() const { return m_Sessions; }; + }; + } } #endif diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 2a6fe44e..3f03ffac 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -12,331 +12,280 @@ #include "I2PService.h" #include -namespace i2p -{ -namespace client -{ - static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519; +namespace i2p { + namespace client { + static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519; - I2PService::I2PService (std::shared_ptr localDestination): - m_LocalDestination (localDestination ? localDestination : - i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)), - m_ReadyTimer(m_LocalDestination->GetService()), - m_ReadyTimerTriggered(false), - m_ConnectTimeout(0), - isUpdated (true) - { - m_LocalDestination->Acquire (); - } + I2PService::I2PService(std::shared_ptr localDestination) : + m_LocalDestination(localDestination ? localDestination : + i2p::client::context.CreateNewLocalDestination(false, I2P_SERVICE_DEFAULT_KEY_TYPE)), + m_ReadyTimer(m_LocalDestination->GetService()), + m_ReadyTimerTriggered(false), + m_ConnectTimeout(0), + isUpdated(true) { + m_LocalDestination->Acquire(); + } - I2PService::I2PService (i2p::data::SigningKeyType kt): - m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)), - m_ReadyTimer(m_LocalDestination->GetService()), - m_ConnectTimeout(0), - isUpdated (true) - { - m_LocalDestination->Acquire (); - } + I2PService::I2PService(i2p::data::SigningKeyType kt) : + m_LocalDestination(i2p::client::context.CreateNewLocalDestination(false, kt)), + m_ReadyTimer(m_LocalDestination->GetService()), + m_ConnectTimeout(0), + isUpdated(true) { + m_LocalDestination->Acquire(); + } - I2PService::~I2PService () - { - ClearHandlers (); - if (m_LocalDestination) m_LocalDestination->Release (); - } + I2PService::~I2PService() { + ClearHandlers(); + if (m_LocalDestination) m_LocalDestination->Release(); + } - void I2PService::ClearHandlers () - { - if(m_ConnectTimeout) - m_ReadyTimer.cancel(); - std::unique_lock l(m_HandlersMutex); - for (auto it: m_Handlers) - it->Terminate (); - m_Handlers.clear(); - } + void I2PService::ClearHandlers() { + if (m_ConnectTimeout) + m_ReadyTimer.cancel(); + std::unique_lock l(m_HandlersMutex); + for (auto it: m_Handlers) + it->Terminate(); + m_Handlers.clear(); + } - void I2PService::SetConnectTimeout(uint32_t timeout) - { - m_ConnectTimeout = timeout; - } + void I2PService::SetConnectTimeout(uint32_t timeout) { + m_ConnectTimeout = timeout; + } - void I2PService::AddReadyCallback(ReadyCallback cb) - { - uint32_t now = i2p::util::GetSecondsSinceEpoch(); - uint32_t tm = (m_ConnectTimeout) ? now + m_ConnectTimeout : NEVER_TIMES_OUT; + void I2PService::AddReadyCallback(ReadyCallback cb) { + uint32_t now = i2p::util::GetSecondsSinceEpoch(); + uint32_t tm = (m_ConnectTimeout) ? now + m_ConnectTimeout : NEVER_TIMES_OUT; - LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now); - m_ReadyCallbacks.push_back({cb, tm}); - if (!m_ReadyTimerTriggered) TriggerReadyCheckTimer(); - } + LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now); + m_ReadyCallbacks.push_back({cb, tm}); + if (!m_ReadyTimerTriggered) TriggerReadyCheckTimer(); + } - void I2PService::TriggerReadyCheckTimer() - { - m_ReadyTimer.expires_from_now(boost::posix_time::seconds (1)); - m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, shared_from_this (), std::placeholders::_1)); - m_ReadyTimerTriggered = true; + void I2PService::TriggerReadyCheckTimer() { + m_ReadyTimer.expires_from_now(boost::posix_time::seconds(1)); + m_ReadyTimer.async_wait( + std::bind(&I2PService::HandleReadyCheckTimer, shared_from_this(), std::placeholders::_1)); + m_ReadyTimerTriggered = true; - } + } - void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec) - { - if(ec || m_LocalDestination->IsReady()) - { - for(auto & itr : m_ReadyCallbacks) - itr.first(ec); - m_ReadyCallbacks.clear(); - } - else if(!m_LocalDestination->IsReady()) - { - // expire timed out requests - uint32_t now = i2p::util::GetSecondsSinceEpoch (); - auto itr = m_ReadyCallbacks.begin(); - while(itr != m_ReadyCallbacks.end()) - { - if(itr->second != NEVER_TIMES_OUT && now >= itr->second) - { - itr->first(boost::asio::error::timed_out); - itr = m_ReadyCallbacks.erase(itr); - } - else - ++itr; - } - } - if(!ec && m_ReadyCallbacks.size()) - TriggerReadyCheckTimer(); - else - m_ReadyTimerTriggered = false; - } + void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec) { + if (ec || m_LocalDestination->IsReady()) { + for (auto &itr: m_ReadyCallbacks) + itr.first(ec); + m_ReadyCallbacks.clear(); + } else if (!m_LocalDestination->IsReady()) { + // expire timed out requests + uint32_t now = i2p::util::GetSecondsSinceEpoch(); + auto itr = m_ReadyCallbacks.begin(); + while (itr != m_ReadyCallbacks.end()) { + if (itr->second != NEVER_TIMES_OUT && now >= itr->second) { + itr->first(boost::asio::error::timed_out); + itr = m_ReadyCallbacks.erase(itr); + } else + ++itr; + } + } + if (!ec && m_ReadyCallbacks.size()) + TriggerReadyCheckTimer(); + else + m_ReadyTimerTriggered = false; + } - void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { - assert(streamRequestComplete); - auto address = i2p::client::context.GetAddressBook ().GetAddress (dest); - if (address) - CreateStream(streamRequestComplete, address, port); - else - { - LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest); - streamRequestComplete (nullptr); - } - } + void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, const std::string &dest, int port) { + assert(streamRequestComplete); + auto address = i2p::client::context.GetAddressBook().GetAddress(dest); + if (address) + CreateStream(streamRequestComplete, address, port); + else { + LogPrint(eLogWarning, "I2PService: Remote destination not found: ", dest); + streamRequestComplete(nullptr); + } + } - void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr address, int port) - { - if(m_ConnectTimeout && !m_LocalDestination->IsReady()) - { - AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) - { - if(ec) - { - LogPrint(eLogWarning, "I2PService::CreateStream() ", ec.message()); - streamRequestComplete(nullptr); - } - else - { - if (address->IsIdentHash ()) - this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); - else - this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); - } - }); - } - else - { - if (address->IsIdentHash ()) - m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port); - else - m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); - } - } + void + I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr address, + int port) { + if (m_ConnectTimeout && !m_LocalDestination->IsReady()) { + AddReadyCallback([this, streamRequestComplete, address, port](const boost::system::error_code &ec) { + if (ec) { + LogPrint(eLogWarning, "I2PService::CreateStream() ", ec.message()); + streamRequestComplete(nullptr); + } else { + if (address->IsIdentHash()) + this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); + else + this->m_LocalDestination->CreateStream(streamRequestComplete, address->blindedPublicKey, + port); + } + }); + } else { + if (address->IsIdentHash()) + m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); + else + m_LocalDestination->CreateStream(streamRequestComplete, address->blindedPublicKey, port); + } + } - TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) - { - boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE); - upstream->set_option(option); - downstream->set_option(option); - } + TCPIPPipe::TCPIPPipe(I2PService *owner, std::shared_ptr upstream, + std::shared_ptr downstream) : I2PServiceHandler(owner), + m_up(upstream), + m_down(downstream) { + boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE); + upstream->set_option(option); + downstream->set_option(option); + } - TCPIPPipe::~TCPIPPipe() - { - Terminate(); - } + TCPIPPipe::~TCPIPPipe() { + Terminate(); + } - void TCPIPPipe::Start() - { - AsyncReceiveUpstream(); - AsyncReceiveDownstream(); - } + void TCPIPPipe::Start() { + AsyncReceiveUpstream(); + AsyncReceiveDownstream(); + } - void TCPIPPipe::Terminate() - { - if(Kill()) return; - if (m_up) - { - if (m_up->is_open()) - m_up->close(); - m_up = nullptr; - } - if (m_down) - { - if (m_down->is_open()) - m_down->close(); - m_down = nullptr; - } - Done(shared_from_this()); - } + void TCPIPPipe::Terminate() { + if (Kill()) return; + if (m_up) { + if (m_up->is_open()) + m_up->close(); + m_up = nullptr; + } + if (m_down) { + if (m_down->is_open()) + m_down->close(); + m_down = nullptr; + } + Done(shared_from_this()); + } - void TCPIPPipe::AsyncReceiveUpstream() - { - if (m_up) - { - m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE), - std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - else - LogPrint(eLogError, "TCPIPPipe: Upstream receive: No socket"); - } + void TCPIPPipe::AsyncReceiveUpstream() { + if (m_up) { + m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE), + std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } else + LogPrint(eLogError, "TCPIPPipe: Upstream receive: No socket"); + } - void TCPIPPipe::AsyncReceiveDownstream() - { - if (m_down) { - m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE), - std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - else - LogPrint(eLogError, "TCPIPPipe: Downstream receive: No socket"); - } + void TCPIPPipe::AsyncReceiveDownstream() { + if (m_down) { + m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE), + std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } else + LogPrint(eLogError, "TCPIPPipe: Downstream receive: No socket"); + } - void TCPIPPipe::UpstreamWrite(size_t len) - { - if (m_up) - { - LogPrint(eLogDebug, "TCPIPPipe: Upstream: ", (int) len, " bytes written"); - boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len), - boost::asio::transfer_all(), - std::bind(&TCPIPPipe::HandleUpstreamWrite, - shared_from_this(), - std::placeholders::_1)); - } - else - LogPrint(eLogError, "TCPIPPipe: Upstream write: no socket"); - } + void TCPIPPipe::UpstreamWrite(size_t len) { + if (m_up) { + LogPrint(eLogDebug, "TCPIPPipe: Upstream: ", (int) len, " bytes written"); + boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len), + boost::asio::transfer_all(), + std::bind(&TCPIPPipe::HandleUpstreamWrite, + shared_from_this(), + std::placeholders::_1)); + } else + LogPrint(eLogError, "TCPIPPipe: Upstream write: no socket"); + } - void TCPIPPipe::DownstreamWrite(size_t len) - { - if (m_down) - { - LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) len, " bytes written"); - boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len), - boost::asio::transfer_all(), - std::bind(&TCPIPPipe::HandleDownstreamWrite, - shared_from_this(), - std::placeholders::_1)); - } - else - LogPrint(eLogError, "TCPIPPipe: Downstream write: No socket"); - } + void TCPIPPipe::DownstreamWrite(size_t len) { + if (m_down) { + LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) len, " bytes written"); + boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len), + boost::asio::transfer_all(), + std::bind(&TCPIPPipe::HandleDownstreamWrite, + shared_from_this(), + std::placeholders::_1)); + } else + LogPrint(eLogError, "TCPIPPipe: Downstream write: No socket"); + } - void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) - { - LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) bytes_transfered, " bytes received"); - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Downstream read error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } else { - if (bytes_transfered > 0 ) - memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered); - UpstreamWrite(bytes_transfered); - } - } + void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code &ecode, std::size_t bytes_transfered) { + LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) bytes_transfered, " bytes received"); + if (ecode) { + LogPrint(eLogError, "TCPIPPipe: Downstream read error:", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else { + if (bytes_transfered > 0) + memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered); + UpstreamWrite(bytes_transfered); + } + } - void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) { - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Downstream write error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } - else - AsyncReceiveUpstream(); - } + void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code &ecode) { + if (ecode) { + LogPrint(eLogError, "TCPIPPipe: Downstream write error:", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else + AsyncReceiveUpstream(); + } - void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) { - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Upstream write error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } - else - AsyncReceiveDownstream(); - } + void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code &ecode) { + if (ecode) { + LogPrint(eLogError, "TCPIPPipe: Upstream write error:", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else + AsyncReceiveDownstream(); + } - void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) - { - LogPrint(eLogDebug, "TCPIPPipe: Upstream ", (int)bytes_transfered, " bytes received"); - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Upstream read error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } else { - if (bytes_transfered > 0 ) - memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered); - DownstreamWrite(bytes_transfered); - } - } + void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code &ecode, std::size_t bytes_transfered) { + LogPrint(eLogDebug, "TCPIPPipe: Upstream ", (int) bytes_transfered, " bytes received"); + if (ecode) { + LogPrint(eLogError, "TCPIPPipe: Upstream read error:", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else { + if (bytes_transfered > 0) + memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered); + DownstreamWrite(bytes_transfered); + } + } - void TCPIPAcceptor::Start () - { - m_Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), m_LocalEndpoint)); - // update the local end point in case port has been set zero and got updated now - m_LocalEndpoint = m_Acceptor->local_endpoint(); - m_Acceptor->listen (); - Accept (); - } + void TCPIPAcceptor::Start() { + m_Acceptor.reset(new boost::asio::ip::tcp::acceptor(GetService(), m_LocalEndpoint)); + // update the local end point in case port has been set zero and got updated now + m_LocalEndpoint = m_Acceptor->local_endpoint(); + m_Acceptor->listen(); + Accept(); + } - void TCPIPAcceptor::Stop () - { - if (m_Acceptor) - { - m_Acceptor->close(); - m_Acceptor.reset (nullptr); - } - m_Timer.cancel (); - ClearHandlers(); - } + void TCPIPAcceptor::Stop() { + if (m_Acceptor) { + m_Acceptor->close(); + m_Acceptor.reset(nullptr); + } + m_Timer.cancel(); + ClearHandlers(); + } - void TCPIPAcceptor::Accept () - { - auto newSocket = std::make_shared (GetService ()); - m_Acceptor->async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this, - std::placeholders::_1, newSocket)); - } + void TCPIPAcceptor::Accept() { + auto newSocket = std::make_shared(GetService()); + m_Acceptor->async_accept(*newSocket, std::bind(&TCPIPAcceptor::HandleAccept, this, + std::placeholders::_1, newSocket)); + } - void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (!ecode) - { - LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted"); - auto handler = CreateHandler(socket); - if (handler) - { - AddHandler(handler); - handler->Handle(); - } - else - socket->close(); - Accept(); - } - else - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ()); - } - } -} + void TCPIPAcceptor::HandleAccept(const boost::system::error_code &ecode, + std::shared_ptr socket) { + if (!ecode) { + LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted"); + auto handler = CreateHandler(socket); + if (handler) { + AddHandler(handler); + handler->Handle(); + } else + socket->close(); + Accept(); + } else { + if (ecode != boost::asio::error::operation_aborted) + LogPrint(eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", + ecode.message()); + } + } + } } diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index e14f85c1..ad6a449a 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.h @@ -18,174 +18,202 @@ #include "Identity.h" #include "AddressBook.h" -namespace i2p -{ -namespace client -{ - class I2PServiceHandler; - class I2PService : public std::enable_shared_from_this - { - public: +namespace i2p { + namespace client { + class I2PServiceHandler; - typedef std::function ReadyCallback; + class I2PService : public std::enable_shared_from_this { + public: - public: + typedef std::function ReadyCallback; - I2PService (std::shared_ptr localDestination = nullptr); - I2PService (i2p::data::SigningKeyType kt); - virtual ~I2PService (); + public: - inline void AddHandler (std::shared_ptr conn) - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.insert(conn); - } - inline void RemoveHandler (std::shared_ptr conn) - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.erase(conn); - } - void ClearHandlers (); + I2PService(std::shared_ptr localDestination = nullptr); - void SetConnectTimeout(uint32_t timeout); + I2PService(i2p::data::SigningKeyType kt); - void AddReadyCallback(ReadyCallback cb); + virtual ~I2PService(); - inline std::shared_ptr GetLocalDestination () { return m_LocalDestination; } - inline std::shared_ptr GetLocalDestination () const { return m_LocalDestination; } - inline void SetLocalDestination (std::shared_ptr dest) - { - if (m_LocalDestination) m_LocalDestination->Release (); - if (dest) dest->Acquire (); - m_LocalDestination = dest; - } - void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0); - void CreateStream(StreamRequestComplete complete, std::shared_ptr address, int port); - inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } + inline void AddHandler(std::shared_ptr conn) { + std::unique_lock l(m_HandlersMutex); + m_Handlers.insert(conn); + } - virtual void Start () = 0; - virtual void Stop () = 0; + inline void RemoveHandler(std::shared_ptr conn) { + std::unique_lock l(m_HandlersMutex); + m_Handlers.erase(conn); + } - virtual const char* GetName() { return "Generic I2P Service"; } + void ClearHandlers(); - private: + void SetConnectTimeout(uint32_t timeout); - void TriggerReadyCheckTimer(); - void HandleReadyCheckTimer(const boost::system::error_code & ec); + void AddReadyCallback(ReadyCallback cb); - private: + inline std::shared_ptr GetLocalDestination() { return m_LocalDestination; } - std::shared_ptr m_LocalDestination; - std::unordered_set > m_Handlers; - std::mutex m_HandlersMutex; - std::vector > m_ReadyCallbacks; - boost::asio::deadline_timer m_ReadyTimer; - bool m_ReadyTimerTriggered; - uint32_t m_ConnectTimeout; + inline std::shared_ptr GetLocalDestination() const { return m_LocalDestination; } - const size_t NEVER_TIMES_OUT = 0; + inline void SetLocalDestination(std::shared_ptr dest) { + if (m_LocalDestination) m_LocalDestination->Release(); + if (dest) dest->Acquire(); + m_LocalDestination = dest; + } - public: + void CreateStream(StreamRequestComplete streamRequestComplete, const std::string &dest, int port = 0); - bool isUpdated; // transient, used during reload only - }; + void CreateStream(StreamRequestComplete complete, std::shared_ptr address, int port); - /*Simple interface for I2PHandlers, allows detection of finalization amongst other things */ - class I2PServiceHandler - { - public: + inline boost::asio::io_service &GetService() { return m_LocalDestination->GetService(); } - I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { } - virtual ~I2PServiceHandler() { } - //If you override this make sure you call it from the children - virtual void Handle() {}; //Start handling the socket + virtual void Start() = 0; - void Terminate () { Kill (); }; + virtual void Stop() = 0; - protected: + virtual const char *GetName() { return "Generic I2P Service"; } - // Call when terminating or handing over to avoid race conditions - inline bool Kill () { return m_Dead.exchange(true); } - // Call to know if the handler is dead - inline bool Dead () { return m_Dead; } - // Call when done to clean up (make sure Kill is called first) - inline void Done (std::shared_ptr me) { if(m_Service) m_Service->RemoveHandler(me); } - // Call to talk with the owner - inline I2PService * GetOwner() { return m_Service; } + private: - private: + void TriggerReadyCheckTimer(); - I2PService *m_Service; - std::atomic m_Dead; //To avoid cleaning up multiple times - }; + void HandleReadyCheckTimer(const boost::system::error_code &ec); - const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8; + private: - // bidirectional pipe for 2 tcp/ip sockets - class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this - { - public: + std::shared_ptr m_LocalDestination; + std::unordered_set > m_Handlers; + std::mutex m_HandlersMutex; + std::vector > m_ReadyCallbacks; + boost::asio::deadline_timer m_ReadyTimer; + bool m_ReadyTimerTriggered; + uint32_t m_ConnectTimeout; - TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream); - ~TCPIPPipe(); - void Start(); + const size_t NEVER_TIMES_OUT = 0; - protected: + public: - void Terminate(); - void AsyncReceiveUpstream(); - void AsyncReceiveDownstream(); - void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred); - void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred); - void HandleUpstreamWrite(const boost::system::error_code & ecode); - void HandleDownstreamWrite(const boost::system::error_code & ecode); - void UpstreamWrite(size_t len); - void DownstreamWrite(size_t len); + bool isUpdated; // transient, used during reload only + }; - private: + /*Simple interface for I2PHandlers, allows detection of finalization amongst other things */ + class I2PServiceHandler { + public: - uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE]; - uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE]; - std::shared_ptr m_up, m_down; - }; + I2PServiceHandler(I2PService *parent) : m_Service(parent), m_Dead(false) {} - /* TODO: support IPv6 too */ - //This is a service that listens for connections on the IP network and interacts with I2P - class TCPIPAcceptor: public I2PService - { - public: + virtual ~I2PServiceHandler() {} - TCPIPAcceptor (const std::string& address, int port, std::shared_ptr localDestination = nullptr) : - I2PService(localDestination), - m_LocalEndpoint (boost::asio::ip::address::from_string(address), port), - m_Timer (GetService ()) {} - TCPIPAcceptor (const std::string& address, int port, i2p::data::SigningKeyType kt) : - I2PService(kt), - m_LocalEndpoint (boost::asio::ip::address::from_string(address), port), - m_Timer (GetService ()) {} - virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); } - //If you override this make sure you call it from the children - void Start (); - //If you override this make sure you call it from the children - void Stop (); + //If you override this make sure you call it from the children + virtual void Handle() {}; //Start handling the socket - const boost::asio::ip::tcp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; + void Terminate() { Kill(); }; - virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } + protected: - protected: + // Call when terminating or handing over to avoid race conditions + inline bool Kill() { return m_Dead.exchange(true); } - virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; + // Call to know if the handler is dead + inline bool Dead() { return m_Dead; } - private: + // Call when done to clean up (make sure Kill is called first) + inline void Done(std::shared_ptr me) { if (m_Service) m_Service->RemoveHandler(me); } - void Accept(); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - boost::asio::ip::tcp::endpoint m_LocalEndpoint; - std::unique_ptr m_Acceptor; - boost::asio::deadline_timer m_Timer; - }; -} + // Call to talk with the owner + inline I2PService *GetOwner() { return m_Service; } + + private: + + I2PService *m_Service; + std::atomic m_Dead; //To avoid cleaning up multiple times + }; + + const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8; + + // bidirectional pipe for 2 tcp/ip sockets + class TCPIPPipe : public I2PServiceHandler, public std::enable_shared_from_this { + public: + + TCPIPPipe(I2PService *owner, std::shared_ptr upstream, + std::shared_ptr downstream); + + ~TCPIPPipe(); + + void Start(); + + protected: + + void Terminate(); + + void AsyncReceiveUpstream(); + + void AsyncReceiveDownstream(); + + void HandleUpstreamReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void HandleDownstreamReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + void HandleUpstreamWrite(const boost::system::error_code &ecode); + + void HandleDownstreamWrite(const boost::system::error_code &ecode); + + void UpstreamWrite(size_t len); + + void DownstreamWrite(size_t len); + + private: + + uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE]; + uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE]; + std::shared_ptr m_up, m_down; + }; + + /* TODO: support IPv6 too */ + //This is a service that listens for connections on the IP network and interacts with I2P + class TCPIPAcceptor : public I2PService { + public: + + TCPIPAcceptor(const std::string &address, int port, + std::shared_ptr localDestination = nullptr) : + I2PService(localDestination), + m_LocalEndpoint(boost::asio::ip::address::from_string(address), port), + m_Timer(GetService()) {} + + TCPIPAcceptor(const std::string &address, int port, i2p::data::SigningKeyType kt) : + I2PService(kt), + m_LocalEndpoint(boost::asio::ip::address::from_string(address), port), + m_Timer(GetService()) {} + + virtual ~TCPIPAcceptor() { TCPIPAcceptor::Stop(); } + + //If you override this make sure you call it from the children + void Start(); + + //If you override this make sure you call it from the children + void Stop(); + + const boost::asio::ip::tcp::endpoint &GetLocalEndpoint() const { return m_LocalEndpoint; }; + + virtual const char *GetName() { return "Generic TCP/IP accepting daemon"; } + + protected: + + virtual std::shared_ptr + CreateHandler(std::shared_ptr socket) = 0; + + private: + + void Accept(); + + void + HandleAccept(const boost::system::error_code &ecode, std::shared_ptr socket); + + boost::asio::ip::tcp::endpoint m_LocalEndpoint; + std::unique_ptr m_Acceptor; + boost::asio::deadline_timer m_Timer; + }; + } } #endif diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index 12c6485f..7d28fffa 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -15,1178 +15,1057 @@ #include "I2PTunnel.h" #include "util.h" -namespace i2p -{ -namespace client -{ +namespace i2p { + namespace client { - /** set standard socket options */ - static void I2PTunnelSetSocketOptions(std::shared_ptr socket) - { - if (socket && socket->is_open()) - { - boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE); - socket->set_option(option); - } - } + /** set standard socket options */ + static void I2PTunnelSetSocketOptions(std::shared_ptr socket) { + if (socket && socket->is_open()) { + boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE); + socket->set_option(option); + } + } - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, int port): - I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), - m_IsQuiet (true) - { - m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); - } + I2PTunnelConnection::I2PTunnelConnection(I2PService *owner, + std::shared_ptr socket, + std::shared_ptr leaseSet, int port) : + I2PServiceHandler(owner), m_Socket(socket), m_RemoteEndpoint(socket->remote_endpoint()), + m_IsQuiet(true) { + m_Stream = GetOwner()->GetLocalDestination()->CreateStream(leaseSet, port); + } - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, - std::shared_ptr socket, std::shared_ptr stream): - I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) - { - } + I2PTunnelConnection::I2PTunnelConnection(I2PService *owner, + std::shared_ptr socket, + std::shared_ptr stream) : + I2PServiceHandler(owner), m_Socket(socket), m_Stream(stream), + m_RemoteEndpoint(socket->remote_endpoint()), m_IsQuiet(true) { + } - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, bool quiet): - I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (target), m_IsQuiet (quiet) - { - } + I2PTunnelConnection::I2PTunnelConnection(I2PService *owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint &target, bool quiet) : + I2PServiceHandler(owner), m_Socket(socket), m_Stream(stream), + m_RemoteEndpoint(target), m_IsQuiet(quiet) { + } - I2PTunnelConnection::~I2PTunnelConnection () - { - } + I2PTunnelConnection::~I2PTunnelConnection() { + } - void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len) - { - if (m_Stream) - { - if (msg) - m_Stream->Send (msg, len); // connect and send - else - m_Stream->Send (m_Buffer, 0); // connect - } - StreamReceive (); - Receive (); - } + void I2PTunnelConnection::I2PConnect(const uint8_t *msg, size_t len) { + if (m_Stream) { + if (msg) + m_Stream->Send(msg, len); // connect and send + else + m_Stream->Send(m_Buffer, 0); // connect + } + StreamReceive(); + Receive(); + } - static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr) - { - boost::asio::ip::address_v4::bytes_type bytes; - const uint8_t * ident = addr; - bytes[0] = 127; - memcpy (bytes.data ()+1, ident, 3); - boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes); - return ourIP; - } + static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash &addr) { + boost::asio::ip::address_v4::bytes_type bytes; + const uint8_t *ident = addr; + bytes[0] = 127; + memcpy(bytes.data() + 1, ident, 3); + boost::asio::ip::address ourIP = boost::asio::ip::address_v4(bytes); + return ourIP; + } #ifdef __linux__ - static void MapToLoopback(const std::shared_ptr & sock, const i2p::data::IdentHash & addr) - { - // bind to 127.x.x.x address - // where x.x.x are first three bytes from ident - auto ourIP = GetLoopbackAddressFor(addr); - boost::system::error_code ec; - sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); - if (ec) - LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); + static void MapToLoopback(const std::shared_ptr & sock, const i2p::data::IdentHash & addr) + { + // bind to 127.x.x.x address + // where x.x.x are first three bytes from ident + auto ourIP = GetLoopbackAddressFor(addr); + boost::system::error_code ec; + sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); + if (ec) + LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); - } + } #endif - void I2PTunnelConnection::Connect (bool isUniqueLocal) - { - I2PTunnelSetSocketOptions(m_Socket); - if (m_Socket) - { + void I2PTunnelConnection::Connect(bool isUniqueLocal) { + I2PTunnelSetSocketOptions(m_Socket); + if (m_Socket) { #ifdef __linux__ - if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () && - m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) - { - m_Socket->open (boost::asio::ip::tcp::v4 ()); - auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash(); - MapToLoopback(m_Socket, ident); - } + if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () && + m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) + { + m_Socket->open (boost::asio::ip::tcp::v4 ()); + auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash(); + MapToLoopback(m_Socket, ident); + } #endif - m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, - shared_from_this (), std::placeholders::_1)); - } - } - - void I2PTunnelConnection::Connect (const boost::asio::ip::address& localAddress) - { - if (m_Socket) - { - if (m_RemoteEndpoint.address().is_v6 ()) - m_Socket->open (boost::asio::ip::tcp::v6 ()); - else - m_Socket->open (boost::asio::ip::tcp::v4 ()); - boost::system::error_code ec; - m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec); - if (ec) - LogPrint (eLogError, "I2PTunnel: Can't bind to ", localAddress.to_string (), ": ", ec.message ()); - } - Connect (false); - } - - void I2PTunnelConnection::Terminate () - { - if (Kill()) return; - if (m_Stream) - { - m_Stream->Close (); - m_Stream.reset (); - } - boost::system::error_code ec; - m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST - m_Socket->close (); - - Done(shared_from_this ()); - } - - void I2PTunnelConnection::Receive () - { - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "I2PTunnel: Read error: ", ecode.message ()); - Terminate (); - } - } - else - WriteToStream (m_Buffer, bytes_transferred); - } - - void I2PTunnelConnection::WriteToStream (const uint8_t * buf, size_t len) - { - if (m_Stream) - { - auto s = shared_from_this (); - m_Stream->AsyncSend (buf, len, - [s](const boost::system::error_code& ecode) - { - if (!ecode) - s->Receive (); - else - s->Terminate (); - }); - } - } - - void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PTunnel: Write error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - StreamReceive (); - } - - void I2PTunnelConnection::StreamReceive () - { - if (m_Stream) - { - if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || - m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular - { - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2), - I2P_TUNNEL_CONNECTION_MAX_IDLE); - } - else // closed by peer - { - // get remaining data - auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE); - if (len > 0) // still some data - Write (m_StreamBuffer, len); - else // no more data - Terminate (); - } - } - } - - void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "I2PTunnel: Stream read error: ", ecode.message ()); - if (bytes_transferred > 0) - Write (m_StreamBuffer, bytes_transferred); // postpone termination - else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) - StreamReceive (); - else - Terminate (); - } - else - Terminate (); - } - else - Write (m_StreamBuffer, bytes_transferred); - } - - void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) - { - boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), - std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); - } - - void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PTunnel: Connect error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "I2PTunnel: Connected"); - if (m_IsQuiet) - StreamReceive (); - else - { - // send destination first like received from I2P - std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 (); - dest += "\n"; - if(sizeof(m_StreamBuffer) >= dest.size()) { - memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); - } - HandleStreamReceive (boost::system::error_code (), dest.size ()); - } - Receive (); - } - } - - void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) - { - if (m_HeaderSent) - I2PTunnelConnection::Write (buf, len); - else - { - m_InHeader.clear (); - m_InHeader.write ((const char *)buf, len); - std::string line; - bool endOfHeader = false; - while (!endOfHeader) - { - std::getline(m_InHeader, line); - if (!m_InHeader.fail ()) - { - if (line == "\r") endOfHeader = true; - else - { - if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) - { - /* close connection, if not Connection: (U|u)pgrade (for websocket) */ - auto x = line.find("pgrade"); - if (x != std::string::npos && std::tolower(line[x - 1]) == 'u') - m_OutHeader << line << "\r\n"; - else - m_OutHeader << "Connection: close\r\n"; - - m_ConnectionSent = true; - } - else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection")) - { - m_OutHeader << "Proxy-Connection: close\r\n"; - m_ProxyConnectionSent = true; - } - else - m_OutHeader << line << "\n"; - } - } - else - break; - } - - if (endOfHeader) - { - if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n"; - if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; - m_OutHeader << "\r\n"; // end of header - m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header - m_InHeader.str (""); - m_HeaderSent = true; - I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); - } - else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) - StreamReceive (); // read more header - else - { - LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); - Terminate (); - } - } - } - - I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host): - I2PTunnelConnection (owner, stream, socket, target), m_Host (host), - m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ()) - { - } - - void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) - { - if (m_HeaderSent) - I2PTunnelConnection::Write (buf, len); - else - { - m_InHeader.clear (); - m_InHeader.write ((const char *)buf, len); - std::string line; - bool endOfHeader = false, connection = false; - while (!endOfHeader) - { - std::getline(m_InHeader, line); - if (!m_InHeader.fail ()) - { - if (line == "\r") endOfHeader = true; - else - { - // strip up some headers - static const std::vector excluded // list of excluded headers - { - "Keep-Alive:", "X-I2P" - }; - bool matched = false; - for (const auto& it: excluded) - if (boost::iequals (line.substr (0, it.length ()), it)) - { - matched = true; - break; - } - if (matched) break; - - // replace some headers - if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:")) - m_OutHeader << "Host: " << m_Host << "\r\n"; // override host - else if (boost::iequals (line.substr (0, 11), "Connection:")) - { - auto x = line.find("pgrade"); - if (x != std::string::npos && x && std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade - m_OutHeader << line << "\n"; - else - m_OutHeader << "Connection: close\r\n"; - connection = true; - } - else // forward as is - m_OutHeader << line << "\n"; - } - } - else - break; - } - - if (endOfHeader) - { - // add Connection if not presented - if (!connection) - m_OutHeader << "Connection: close\r\n"; - // add X-I2P fields - if (m_From) - { - m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n"; - m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n"; - m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n"; - } - - m_OutHeader << "\r\n"; // end of header - m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header - m_InHeader.str (""); - m_From = nullptr; - m_HeaderSent = true; - I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); - } - else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) - StreamReceive (); // read more header - else - { - LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); - Terminate (); - } - } - } - - void I2PServerTunnelConnectionHTTP::WriteToStream (const uint8_t * buf, size_t len) - { - if (m_ResponseHeaderSent) - I2PTunnelConnection::WriteToStream (buf, len); - else - { - m_InHeader.clear (); - if (m_InHeader.str ().empty ()) m_OutHeader.str (""); // start of response - m_InHeader.write ((const char *)buf, len); - std::string line; - bool endOfHeader = false; - while (!endOfHeader) - { - std::getline(m_InHeader, line); - if (!m_InHeader.fail ()) - { - if (line == "\r") endOfHeader = true; - else - { - static const std::vector excluded // list of excluded headers - { - "Server:", "Date:", "X-Runtime:", "X-Powered-By:", "Proxy" - }; - bool matched = false; - for (const auto& it: excluded) - if (!line.compare(0, it.length (), it)) - { - matched = true; - break; - } - if (!matched) - m_OutHeader << line << "\n"; - } - } - else - break; - } - - if (endOfHeader) - { - m_OutHeader << "\r\n"; // end of header - m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header - m_InHeader.str (""); - m_ResponseHeaderSent = true; - I2PTunnelConnection::WriteToStream ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); - m_OutHeader.str (""); - } - else - Receive (); - } - } - - I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass): - I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()), - m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) - { - } - - void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len) - { - m_OutPacket.str (""); - if (m_NeedsWebIrc) - { - m_NeedsWebIrc = false; - m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl; - } - - m_InPacket.clear (); - m_InPacket.write ((const char *)buf, len); - - while (!m_InPacket.eof () && !m_InPacket.fail ()) - { - std::string line; - std::getline (m_InPacket, line); - if (line.length () == 0 && m_InPacket.eof ()) - m_InPacket.str (""); - auto pos = line.find ("USER"); - if (!pos) // start of line - { - pos = line.find (" "); - pos++; - pos = line.find (" ", pos); - pos++; - auto nextpos = line.find (" ", pos); - m_OutPacket << line.substr (0, pos); - m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()); - m_OutPacket << line.substr (nextpos) << '\n'; - } - else - m_OutPacket << line << '\n'; - } - I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ()); - } - - - /* This handler tries to establish a connection with the desired server and dies if it fails to do so */ - class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this - { - public: - I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr address, - int destinationPort, std::shared_ptr socket): - I2PServiceHandler(parent), m_Address(address), - m_DestinationPort (destinationPort), m_Socket(socket) {}; - void Handle(); - void Terminate(); - private: - void HandleStreamRequestComplete (std::shared_ptr stream); - std::shared_ptr m_Address; - int m_DestinationPort; - std::shared_ptr m_Socket; - }; - - void I2PClientTunnelHandler::Handle() - { - GetOwner()->CreateStream ( - std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), - m_Address, m_DestinationPort); - } - - void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - if (Kill()) return; - LogPrint (eLogDebug, "I2PTunnel: New connection"); - auto connection = std::make_shared(GetOwner(), m_Socket, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info."); - Terminate(); - } - } - - void I2PClientTunnelHandler::Terminate() - { - if (Kill()) return; - if (m_Socket) - { - m_Socket->close(); - m_Socket = nullptr; - } - Done(shared_from_this()); - } - - I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, - const std::string& address, int port, std::shared_ptr localDestination, int destinationPort): - TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), - m_DestinationPort (destinationPort), m_KeepAliveInterval (0) - { - } - - void I2PClientTunnel::Start () - { - TCPIPAcceptor::Start (); - GetAddress (); - if (m_KeepAliveInterval) - ScheduleKeepAliveTimer (); - } - - void I2PClientTunnel::Stop () - { - TCPIPAcceptor::Stop(); - m_Address = nullptr; - if (m_KeepAliveTimer) m_KeepAliveTimer->cancel (); - } - - void I2PClientTunnel::SetKeepAliveInterval (uint32_t keepAliveInterval) - { - m_KeepAliveInterval = keepAliveInterval; - if (m_KeepAliveInterval) - m_KeepAliveTimer.reset (new boost::asio::deadline_timer (GetLocalDestination ()->GetService ())); - } - - /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ - std::shared_ptr I2PClientTunnel::GetAddress () - { - if (!m_Address) - { - m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination); - if (!m_Address) - LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found"); - } - return m_Address; - } - - std::shared_ptr I2PClientTunnel::CreateHandler(std::shared_ptr socket) - { - auto address = GetAddress (); - if (address) - return std::make_shared(this, address, m_DestinationPort, socket); - else - return nullptr; - } - - void I2PClientTunnel::ScheduleKeepAliveTimer () - { - if (m_KeepAliveTimer) - { - m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds (m_KeepAliveInterval)); - m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer, - this, std::placeholders::_1)); - } - } - - void I2PClientTunnel::HandleKeepAliveTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_Address && m_Address->IsValid ()) - { - if (m_Address->IsIdentHash ()) - GetLocalDestination ()->SendPing (m_Address->identHash); - else - GetLocalDestination ()->SendPing (m_Address->blindedPublicKey); - } - ScheduleKeepAliveTimer (); - } - } - - I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, - int port, std::shared_ptr localDestination, int inport, bool gzip): - I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) - { - m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip); - } - - void I2PServerTunnel::Start () - { - m_Endpoint.port (m_Port); - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string (m_Address, ec); - if (!ec) - { - m_Endpoint.address (addr); - Accept (); - } - else - { - auto resolver = std::make_shared(GetService ()); - resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""), - std::bind (&I2PServerTunnel::HandleResolve, this, - std::placeholders::_1, std::placeholders::_2, resolver)); - } - } - - void I2PServerTunnel::Stop () - { - if (m_PortDestination) - m_PortDestination->ResetAcceptor (); - auto localDestination = GetLocalDestination (); - if (localDestination) - localDestination->StopAcceptingStreams (); - - ClearHandlers (); - } - - void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, - std::shared_ptr resolver) - { - if (!ecode) - { - bool found = false; - boost::asio::ip::tcp::endpoint ep; - if (m_LocalAddress) - { - boost::asio::ip::tcp::resolver::iterator end; - while (it != end) - { - ep = *it; - if (!ep.address ().is_unspecified ()) - { - if (ep.address ().is_v4 ()) - { - if (m_LocalAddress->is_v4 ()) found = true; - } - else if (ep.address ().is_v6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (ep.address ())) - { - if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress)) - found = true; - } - else if (m_LocalAddress->is_v6 ()) - found = true; - } - } - if (found) break; - it++; - } - } - else - { - found = true; - ep = *it; // first available - } - if (!found) - { - LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address"); - return; - } - - auto addr = ep.address (); - LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*it).host_name (), " has been resolved to ", addr); - m_Endpoint.address (addr); - Accept (); - } - else - LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ()); - } - - void I2PServerTunnel::SetAccessList (const std::set& accessList) - { - m_AccessList = accessList; - m_IsAccessList = true; - } - - void I2PServerTunnel::SetLocalAddress (const std::string& localAddress) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string(localAddress, ec); - if (!ec) - m_LocalAddress.reset (new boost::asio::ip::address (addr)); - else - LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress); - } - - void I2PServerTunnel::Accept () - { - if (m_PortDestination) - m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); - - auto localDestination = GetLocalDestination (); - if (localDestination) - { - if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet - localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); - } - else - LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel"); - } - - void I2PServerTunnel::HandleAccept (std::shared_ptr stream) - { - if (stream) - { - if (m_IsAccessList) - { - if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ())) - { - LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped"); - stream->Close (); - return; - } - } - // new connection - auto conn = CreateI2PConnection (stream); - AddHandler (conn); - if (m_LocalAddress) - conn->Connect (*m_LocalAddress); - else - conn->Connect (m_IsUniqueLocal); - } - } - - std::shared_ptr I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) - { - return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); - - } - - I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, - int port, std::shared_ptr localDestination, - const std::string& host, int inport, bool gzip): - I2PServerTunnel (name, address, port, localDestination, inport, gzip), - m_Host (host) - { - } - - std::shared_ptr I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) - { - return std::make_shared (this, stream, - std::make_shared (GetService ()), GetEndpoint (), m_Host); - } - - I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, - int port, std::shared_ptr localDestination, - const std::string& webircpass, int inport, bool gzip): - I2PServerTunnel (name, address, port, localDestination, inport, gzip), - m_WebircPass (webircpass) - { - } - - std::shared_ptr I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr stream) - { - return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), this->m_WebircPass); - } - - void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash ().GetLL()[0] || fromPort != m_LastSession->RemotePort) - { - std::lock_guard lock(m_SessionsMutex); - m_LastSession = ObtainUDPSession(from, toPort, fromPort); - } - m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); - m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - } - - void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t, uint16_t, const uint8_t * buf, size_t len) - { - if (m_LastSession) - { - m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); - m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - } - } - - void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) - { - std::lock_guard lock(m_SessionsMutex); - uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - auto itr = m_Sessions.begin(); - while(itr != m_Sessions.end()) { - if(now - (*itr)->LastActivity >= delta ) - itr = m_Sessions.erase(itr); - else - ++itr; - } - } - - void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) - { - std::lock_guard lock(m_SessionsMutex); - uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - std::vector removePorts; - for (const auto & s : m_Sessions) { - if (now - s.second->second >= delta) - removePorts.push_back(s.first); - } - for(auto port : removePorts) { - m_Sessions.erase(port); - } - } - - UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) - { - auto ih = from.GetIdentHash(); - for (auto & s : m_Sessions ) - { - if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort) - { - /** found existing session */ - LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); - return s; - } - } - boost::asio::ip::address addr; - /** create new udp session */ - if(m_IsUniqueLocal && m_LocalAddress.is_loopback()) - { - auto ident = from.GetIdentHash(); - addr = GetLoopbackAddressFor(ident); - } - else - addr = m_LocalAddress; - boost::asio::ip::udp::endpoint ep(addr, 0); - m_Sessions.push_back(std::make_shared(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); - auto & back = m_Sessions.back(); - return back; - } - - UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, - const std::shared_ptr & localDestination, - boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to, - uint16_t ourPort, uint16_t theirPort) : - m_Destination(localDestination->GetDatagramDestination()), - IPSocket(localDestination->GetService(), localEndpoint), - SendEndpoint(endpoint), - LastActivity(i2p::util::GetMillisecondsSinceEpoch()), - LocalPort(ourPort), - RemotePort(theirPort) - { - IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU )); - memcpy(Identity, to->data(), 32); - Receive(); - } - - void UDPSession::Receive() - { - LogPrint(eLogDebug, "UDPSession: Receive"); - IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU), - FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2)); - } - - void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len) - { - if(!ecode) - { - LogPrint(eLogDebug, "UDPSession: Forward ", len, "B from ", FromEndpoint); - auto ts = i2p::util::GetMillisecondsSinceEpoch(); - auto session = m_Destination->GetSession (Identity); - if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort); - else - m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort); - size_t numPackets = 0; - while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) - { - boost::system::error_code ec; - size_t moreBytes = IPSocket.available(ec); - if (ec || !moreBytes) break; - len = IPSocket.receive_from (boost::asio::buffer (m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec); - m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort); - numPackets++; - } - if (numPackets > 0) - LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint); - m_Destination->FlushSendQueue (session); - LastActivity = ts; - Receive(); - } - else - LogPrint(eLogError, "UDPSession: ", ecode.message()); - } - - I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr localDestination, - boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) : - m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress), - m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip) - { - } - - I2PUDPServerTunnel::~I2PUDPServerTunnel () - { - Stop (); - } - - void I2PUDPServerTunnel::Start () - { - m_LocalDest->Start (); - - auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); - dgram->SetReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - } - - void I2PUDPServerTunnel::Stop () - { - auto dgram = m_LocalDest->GetDatagramDestination (); - if (dgram) dgram->ResetReceiver (); - } - - std::vector > I2PUDPServerTunnel::GetSessions () - { - std::vector > sessions; - std::lock_guard lock (m_SessionsMutex); - - for (UDPSessionPtr s: m_Sessions) - { - if (!s->m_Destination) continue; - auto info = s->m_Destination->GetInfoForRemote (s->Identity); - if (!info) continue; - - auto sinfo = std::make_shared (); - sinfo->Name = m_Name; - sinfo->LocalIdent = std::make_shared (m_LocalDest->GetIdentHash ().data ()); - sinfo->RemoteIdent = std::make_shared (s->Identity.data ()); - sinfo->CurrentIBGW = info->IBGW; - sinfo->CurrentOBEP = info->OBEP; - sessions.push_back (sinfo); - } - return sessions; - } - - I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest, - boost::asio::ip::udp::endpoint localEndpoint, - std::shared_ptr localDestination, - uint16_t remotePort, bool gzip) : - m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint), - m_RemoteIdent (nullptr), m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort), - m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip) - { - } - - I2PUDPClientTunnel::~I2PUDPClientTunnel () - { - Stop (); - } - - void I2PUDPClientTunnel::Start () - { - // Reset flag in case of tunnel reload - if (m_cancel_resolve) m_cancel_resolve = false; - - m_LocalSocket.reset (new boost::asio::ip::udp::socket (m_LocalDest->GetService (), m_LocalEndpoint)); - m_LocalSocket->set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU)); - m_LocalSocket->set_option (boost::asio::socket_base::reuse_address (true)); - - auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); - dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3, std::placeholders::_4, - std::placeholders::_5)); - dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - - m_LocalDest->Start (); - if (m_ResolveThread == nullptr) - m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this)); - RecvFromLocal (); - } - - void I2PUDPClientTunnel::Stop () - { - auto dgram = m_LocalDest->GetDatagramDestination (); - if (dgram) dgram->ResetReceiver (); - m_cancel_resolve = true; - - m_Sessions.clear(); - - if(m_LocalSocket && m_LocalSocket->is_open ()) - m_LocalSocket->close (); - - if(m_ResolveThread) - { - m_ResolveThread->join (); - delete m_ResolveThread; - m_ResolveThread = nullptr; - } - if (m_RemoteIdent) - { - delete m_RemoteIdent; - m_RemoteIdent = nullptr; - } - } - - void I2PUDPClientTunnel::RecvFromLocal () - { - m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), - m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2)); - } - - void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred) - { - if (m_cancel_resolve) { - LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping"); - return; - } - if (ec) { - LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener..."); - RecvFromLocal (); // Restart listener and continue work - return; - } - if (!m_RemoteIdent) { - LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet"); - RecvFromLocal (); - return; // drop, remote not resolved - } - auto remotePort = m_RecvEndpoint.port (); - if (!m_LastPort || m_LastPort != remotePort) - { - auto itr = m_Sessions.find (remotePort); - if (itr != m_Sessions.end ()) - m_LastSession = itr->second; - else - { - m_LastSession = std::make_shared (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0); - m_Sessions.emplace (remotePort, m_LastSession); - } - m_LastPort = remotePort; - } - // send off to remote i2p destination - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32 (), ":", RemotePort); - auto session = m_LocalDest->GetDatagramDestination ()->GetSession (*m_RemoteIdent); - if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); - else - m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); - size_t numPackets = 0; - while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) - { - boost::system::error_code ec; - size_t moreBytes = m_LocalSocket->available (ec); - if (ec || !moreBytes) break; - transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec); - remotePort = m_RecvEndpoint.port (); - // TODO: check remotePort - m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); - numPackets++; - } - if (numPackets) - LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32 ()); - m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session); - - // mark convo as active - if (m_LastSession) - m_LastSession->second = ts; - RecvFromLocal (); - } - - std::vector > I2PUDPClientTunnel::GetSessions () - { - // TODO: implement - std::vector > infos; - return infos; - } - - void I2PUDPClientTunnel::TryResolving () - { - i2p::util::SetThreadName ("UDP Resolver"); - LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); - - std::shared_ptr addr; - while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve) - { - LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest); - std::this_thread::sleep_for (std::chrono::seconds (1)); - } - if (m_cancel_resolve) - { - LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled"); - return; - } - if (!addr || !addr->IsIdentHash ()) - { - LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found"); - return; - } - m_RemoteIdent = new i2p::data::IdentHash; - *m_RemoteIdent = addr->identHash; - LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32 ()); - } - - void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) - HandleRecvFromI2PRaw (fromPort, toPort, buf, len); - else - LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32 ()); - } - - void I2PUDPClientTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - auto itr = m_Sessions.find (toPort); - // found convo ? - if (itr != m_Sessions.end ()) - { - // found convo - if (len > 0) - { - LogPrint (eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32 () : ""); - m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first); - // mark convo as active - itr->second->second = i2p::util::GetMillisecondsSinceEpoch (); - } - } - else - LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort); - } -} + m_Socket->async_connect(m_RemoteEndpoint, std::bind(&I2PTunnelConnection::HandleConnect, + shared_from_this(), std::placeholders::_1)); + } + } + + void I2PTunnelConnection::Connect(const boost::asio::ip::address &localAddress) { + if (m_Socket) { + if (m_RemoteEndpoint.address().is_v6()) + m_Socket->open(boost::asio::ip::tcp::v6()); + else + m_Socket->open(boost::asio::ip::tcp::v4()); + boost::system::error_code ec; + m_Socket->bind(boost::asio::ip::tcp::endpoint(localAddress, 0), ec); + if (ec) + LogPrint(eLogError, "I2PTunnel: Can't bind to ", localAddress.to_string(), ": ", ec.message()); + } + Connect(false); + } + + void I2PTunnelConnection::Terminate() { + if (Kill()) return; + if (m_Stream) { + m_Stream->Close(); + m_Stream.reset(); + } + boost::system::error_code ec; + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST + m_Socket->close(); + + Done(shared_from_this()); + } + + void I2PTunnelConnection::Receive() { + m_Socket->async_read_some(boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + + void + I2PTunnelConnection::HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogError, "I2PTunnel: Read error: ", ecode.message()); + Terminate(); + } + } else + WriteToStream(m_Buffer, bytes_transferred); + } + + void I2PTunnelConnection::WriteToStream(const uint8_t *buf, size_t len) { + if (m_Stream) { + auto s = shared_from_this(); + m_Stream->AsyncSend(buf, len, + [s](const boost::system::error_code &ecode) { + if (!ecode) + s->Receive(); + else + s->Terminate(); + }); + } + } + + void I2PTunnelConnection::HandleWrite(const boost::system::error_code &ecode) { + if (ecode) { + LogPrint(eLogError, "I2PTunnel: Write error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate(); + } else + StreamReceive(); + } + + void I2PTunnelConnection::StreamReceive() { + if (m_Stream) { + if (m_Stream->GetStatus() == i2p::stream::eStreamStatusNew || + m_Stream->GetStatus() == i2p::stream::eStreamStatusOpen) // regular + { + m_Stream->AsyncReceive(boost::asio::buffer(m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind(&I2PTunnelConnection::HandleStreamReceive, shared_from_this(), + std::placeholders::_1, std::placeholders::_2), + I2P_TUNNEL_CONNECTION_MAX_IDLE); + } else // closed by peer + { + // get remaining data + auto len = m_Stream->ReadSome(m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE); + if (len > 0) // still some data + Write(m_StreamBuffer, len); + else // no more data + Terminate(); + } + } + } + + void I2PTunnelConnection::HandleStreamReceive(const boost::system::error_code &ecode, + std::size_t bytes_transferred) { + if (ecode) { + if (ecode != boost::asio::error::operation_aborted) { + LogPrint(eLogError, "I2PTunnel: Stream read error: ", ecode.message()); + if (bytes_transferred > 0) + Write(m_StreamBuffer, bytes_transferred); // postpone termination + else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen()) + StreamReceive(); + else + Terminate(); + } else + Terminate(); + } else + Write(m_StreamBuffer, bytes_transferred); + } + + void I2PTunnelConnection::Write(const uint8_t *buf, size_t len) { + boost::asio::async_write(*m_Socket, boost::asio::buffer(buf, len), boost::asio::transfer_all(), + std::bind(&I2PTunnelConnection::HandleWrite, shared_from_this(), + std::placeholders::_1)); + } + + void I2PTunnelConnection::HandleConnect(const boost::system::error_code &ecode) { + if (ecode) { + LogPrint(eLogError, "I2PTunnel: Connect error: ", ecode.message()); + Terminate(); + } else { + LogPrint(eLogDebug, "I2PTunnel: Connected"); + if (m_IsQuiet) + StreamReceive(); + else { + // send destination first like received from I2P + std::string dest = m_Stream->GetRemoteIdentity()->ToBase64(); + dest += "\n"; + if (sizeof(m_StreamBuffer) >= dest.size()) { + memcpy(m_StreamBuffer, dest.c_str(), dest.size()); + } + HandleStreamReceive(boost::system::error_code(), dest.size()); + } + Receive(); + } + } + + void I2PClientTunnelConnectionHTTP::Write(const uint8_t *buf, size_t len) { + if (m_HeaderSent) + I2PTunnelConnection::Write(buf, len); + else { + m_InHeader.clear(); + m_InHeader.write((const char *) buf, len); + std::string line; + bool endOfHeader = false; + while (!endOfHeader) { + std::getline(m_InHeader, line); + if (!m_InHeader.fail()) { + if (line == "\r") endOfHeader = true; + else { + if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) { + /* close connection, if not Connection: (U|u)pgrade (for websocket) */ + auto x = line.find("pgrade"); + if (x != std::string::npos && std::tolower(line[x - 1]) == 'u') + m_OutHeader << line << "\r\n"; + else + m_OutHeader << "Connection: close\r\n"; + + m_ConnectionSent = true; + } else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection")) { + m_OutHeader << "Proxy-Connection: close\r\n"; + m_ProxyConnectionSent = true; + } else + m_OutHeader << line << "\n"; + } + } else + break; + } + + if (endOfHeader) { + if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n"; + if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; + m_OutHeader << "\r\n"; // end of header + m_OutHeader << m_InHeader.str().substr(m_InHeader.tellg()); // data right after header + m_InHeader.str(""); + m_HeaderSent = true; + I2PTunnelConnection::Write((uint8_t *) m_OutHeader.str().c_str(), m_OutHeader.str().length()); + } else if (m_OutHeader.tellp() < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) + StreamReceive(); // read more header + else { + LogPrint(eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); + Terminate(); + } + } + } + + I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP(I2PService *owner, + std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint &target, + const std::string &host) : + I2PTunnelConnection(owner, stream, socket, target), m_Host(host), + m_HeaderSent(false), m_ResponseHeaderSent(false), m_From(stream->GetRemoteIdentity()) { + } + + void I2PServerTunnelConnectionHTTP::Write(const uint8_t *buf, size_t len) { + if (m_HeaderSent) + I2PTunnelConnection::Write(buf, len); + else { + m_InHeader.clear(); + m_InHeader.write((const char *) buf, len); + std::string line; + bool endOfHeader = false, connection = false; + while (!endOfHeader) { + std::getline(m_InHeader, line); + if (!m_InHeader.fail()) { + if (line == "\r") endOfHeader = true; + else { + // strip up some headers + static const std::vector excluded // list of excluded headers + { + "Keep-Alive:", "X-I2P" + }; + bool matched = false; + for (const auto &it: excluded) + if (boost::iequals(line.substr(0, it.length()), it)) { + matched = true; + break; + } + if (matched) break; + + // replace some headers + if (!m_Host.empty() && boost::iequals(line.substr(0, 5), "Host:")) + m_OutHeader << "Host: " << m_Host << "\r\n"; // override host + else if (boost::iequals(line.substr(0, 11), "Connection:")) { + auto x = line.find("pgrade"); + if (x != std::string::npos && x && + std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade + m_OutHeader << line << "\n"; + else + m_OutHeader << "Connection: close\r\n"; + connection = true; + } else // forward as is + m_OutHeader << line << "\n"; + } + } else + break; + } + + if (endOfHeader) { + // add Connection if not presented + if (!connection) + m_OutHeader << "Connection: close\r\n"; + // add X-I2P fields + if (m_From) { + m_OutHeader << X_I2P_DEST_B32 << ": " + << context.GetAddressBook().ToAddress(m_From->GetIdentHash()) << "\r\n"; + m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash().ToBase64() << "\r\n"; + m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64() << "\r\n"; + } + + m_OutHeader << "\r\n"; // end of header + m_OutHeader << m_InHeader.str().substr(m_InHeader.tellg()); // data right after header + m_InHeader.str(""); + m_From = nullptr; + m_HeaderSent = true; + I2PTunnelConnection::Write((uint8_t *) m_OutHeader.str().c_str(), m_OutHeader.str().length()); + } else if (m_OutHeader.tellp() < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) + StreamReceive(); // read more header + else { + LogPrint(eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); + Terminate(); + } + } + } + + void I2PServerTunnelConnectionHTTP::WriteToStream(const uint8_t *buf, size_t len) { + if (m_ResponseHeaderSent) + I2PTunnelConnection::WriteToStream(buf, len); + else { + m_InHeader.clear(); + if (m_InHeader.str().empty()) m_OutHeader.str(""); // start of response + m_InHeader.write((const char *) buf, len); + std::string line; + bool endOfHeader = false; + while (!endOfHeader) { + std::getline(m_InHeader, line); + if (!m_InHeader.fail()) { + if (line == "\r") endOfHeader = true; + else { + static const std::vector excluded // list of excluded headers + { + "Server:", "Date:", "X-Runtime:", "X-Powered-By:", "Proxy" + }; + bool matched = false; + for (const auto &it: excluded) + if (!line.compare(0, it.length(), it)) { + matched = true; + break; + } + if (!matched) + m_OutHeader << line << "\n"; + } + } else + break; + } + + if (endOfHeader) { + m_OutHeader << "\r\n"; // end of header + m_OutHeader << m_InHeader.str().substr(m_InHeader.tellg()); // data right after header + m_InHeader.str(""); + m_ResponseHeaderSent = true; + I2PTunnelConnection::WriteToStream((uint8_t *) m_OutHeader.str().c_str(), + m_OutHeader.str().length()); + m_OutHeader.str(""); + } else + Receive(); + } + } + + I2PTunnelConnectionIRC::I2PTunnelConnectionIRC(I2PService *owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint &target, + const std::string &webircpass) : + I2PTunnelConnection(owner, stream, socket, target), m_From(stream->GetRemoteIdentity()), + m_NeedsWebIrc(webircpass.length() ? true : false), m_WebircPass(webircpass) { + } + + void I2PTunnelConnectionIRC::Write(const uint8_t *buf, size_t len) { + m_OutPacket.str(""); + if (m_NeedsWebIrc) { + m_NeedsWebIrc = false; + m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " + << context.GetAddressBook().ToAddress(m_From->GetIdentHash()) << " " + << GetSocket()->local_endpoint().address() << std::endl; + } + + m_InPacket.clear(); + m_InPacket.write((const char *) buf, len); + + while (!m_InPacket.eof() && !m_InPacket.fail()) { + std::string line; + std::getline(m_InPacket, line); + if (line.length() == 0 && m_InPacket.eof()) + m_InPacket.str(""); + auto pos = line.find("USER"); + if (!pos) // start of line + { + pos = line.find(" "); + pos++; + pos = line.find(" ", pos); + pos++; + auto nextpos = line.find(" ", pos); + m_OutPacket << line.substr(0, pos); + m_OutPacket << context.GetAddressBook().ToAddress(m_From->GetIdentHash()); + m_OutPacket << line.substr(nextpos) << '\n'; + } else + m_OutPacket << line << '\n'; + } + I2PTunnelConnection::Write((uint8_t *) m_OutPacket.str().c_str(), m_OutPacket.str().length()); + } + + + /* This handler tries to establish a connection with the desired server and dies if it fails to do so */ + class I2PClientTunnelHandler + : public I2PServiceHandler, public std::enable_shared_from_this { + public: + I2PClientTunnelHandler(I2PClientTunnel *parent, std::shared_ptr address, + int destinationPort, std::shared_ptr socket) : + I2PServiceHandler(parent), m_Address(address), + m_DestinationPort(destinationPort), m_Socket(socket) {}; + + void Handle(); + + void Terminate(); + + private: + void HandleStreamRequestComplete(std::shared_ptr stream); + + std::shared_ptr m_Address; + int m_DestinationPort; + std::shared_ptr m_Socket; + }; + + void I2PClientTunnelHandler::Handle() { + GetOwner()->CreateStream( + std::bind(&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), + std::placeholders::_1), + m_Address, m_DestinationPort); + } + + void I2PClientTunnelHandler::HandleStreamRequestComplete(std::shared_ptr stream) { + if (stream) { + if (Kill()) return; + LogPrint(eLogDebug, "I2PTunnel: New connection"); + auto connection = std::make_shared(GetOwner(), m_Socket, stream); + GetOwner()->AddHandler(connection); + connection->I2PConnect(); + Done(shared_from_this()); + } else { + LogPrint(eLogError, + "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info."); + Terminate(); + } + } + + void I2PClientTunnelHandler::Terminate() { + if (Kill()) return; + if (m_Socket) { + m_Socket->close(); + m_Socket = nullptr; + } + Done(shared_from_this()); + } + + I2PClientTunnel::I2PClientTunnel(const std::string &name, const std::string &destination, + const std::string &address, int port, + std::shared_ptr localDestination, int destinationPort) : + TCPIPAcceptor(address, port, localDestination), m_Name(name), m_Destination(destination), + m_DestinationPort(destinationPort), m_KeepAliveInterval(0) { + } + + void I2PClientTunnel::Start() { + TCPIPAcceptor::Start(); + GetAddress(); + if (m_KeepAliveInterval) + ScheduleKeepAliveTimer(); + } + + void I2PClientTunnel::Stop() { + TCPIPAcceptor::Stop(); + m_Address = nullptr; + if (m_KeepAliveTimer) m_KeepAliveTimer->cancel(); + } + + void I2PClientTunnel::SetKeepAliveInterval(uint32_t keepAliveInterval) { + m_KeepAliveInterval = keepAliveInterval; + if (m_KeepAliveInterval) + m_KeepAliveTimer.reset(new boost::asio::deadline_timer(GetLocalDestination()->GetService())); + } + + /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ + std::shared_ptr I2PClientTunnel::GetAddress() { + if (!m_Address) { + m_Address = i2p::client::context.GetAddressBook().GetAddress(m_Destination); + if (!m_Address) + LogPrint(eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found"); + } + return m_Address; + } + + std::shared_ptr + I2PClientTunnel::CreateHandler(std::shared_ptr socket) { + auto address = GetAddress(); + if (address) + return std::make_shared(this, address, m_DestinationPort, socket); + else + return nullptr; + } + + void I2PClientTunnel::ScheduleKeepAliveTimer() { + if (m_KeepAliveTimer) { + m_KeepAliveTimer->expires_from_now(boost::posix_time::seconds(m_KeepAliveInterval)); + m_KeepAliveTimer->async_wait(std::bind(&I2PClientTunnel::HandleKeepAliveTimer, + this, std::placeholders::_1)); + } + } + + void I2PClientTunnel::HandleKeepAliveTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + if (m_Address && m_Address->IsValid()) { + if (m_Address->IsIdentHash()) + GetLocalDestination()->SendPing(m_Address->identHash); + else + GetLocalDestination()->SendPing(m_Address->blindedPublicKey); + } + ScheduleKeepAliveTimer(); + } + } + + I2PServerTunnel::I2PServerTunnel(const std::string &name, const std::string &address, + int port, std::shared_ptr localDestination, int inport, + bool gzip) : + I2PService(localDestination), m_IsUniqueLocal(true), m_Name(name), m_Address(address), m_Port(port), + m_IsAccessList(false) { + m_PortDestination = localDestination->CreateStreamingDestination(inport > 0 ? inport : port, gzip); + } + + void I2PServerTunnel::Start() { + m_Endpoint.port(m_Port); + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string(m_Address, ec); + if (!ec) { + m_Endpoint.address(addr); + Accept(); + } else { + auto resolver = std::make_shared(GetService()); + resolver->async_resolve(boost::asio::ip::tcp::resolver::query(m_Address, ""), + std::bind(&I2PServerTunnel::HandleResolve, this, + std::placeholders::_1, std::placeholders::_2, resolver)); + } + } + + void I2PServerTunnel::Stop() { + if (m_PortDestination) + m_PortDestination->ResetAcceptor(); + auto localDestination = GetLocalDestination(); + if (localDestination) + localDestination->StopAcceptingStreams(); + + ClearHandlers(); + } + + void I2PServerTunnel::HandleResolve(const boost::system::error_code &ecode, + boost::asio::ip::tcp::resolver::iterator it, + std::shared_ptr resolver) { + if (!ecode) { + bool found = false; + boost::asio::ip::tcp::endpoint ep; + if (m_LocalAddress) { + boost::asio::ip::tcp::resolver::iterator end; + while (it != end) { + ep = *it; + if (!ep.address().is_unspecified()) { + if (ep.address().is_v4()) { + if (m_LocalAddress->is_v4()) found = true; + } else if (ep.address().is_v6()) { + if (i2p::util::net::IsYggdrasilAddress(ep.address())) { + if (i2p::util::net::IsYggdrasilAddress(*m_LocalAddress)) + found = true; + } else if (m_LocalAddress->is_v6()) + found = true; + } + } + if (found) break; + it++; + } + } else { + found = true; + ep = *it; // first available + } + if (!found) { + LogPrint(eLogError, "I2PTunnel: Unable to resolve to compatible address"); + return; + } + + auto addr = ep.address(); + LogPrint(eLogInfo, "I2PTunnel: Server tunnel ", (*it).host_name(), " has been resolved to ", addr); + m_Endpoint.address(addr); + Accept(); + } else + LogPrint(eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message()); + } + + void I2PServerTunnel::SetAccessList(const std::set &accessList) { + m_AccessList = accessList; + m_IsAccessList = true; + } + + void I2PServerTunnel::SetLocalAddress(const std::string &localAddress) { + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string(localAddress, ec); + if (!ec) + m_LocalAddress.reset(new boost::asio::ip::address(addr)); + else + LogPrint(eLogError, "I2PTunnel: Can't set local address ", localAddress); + } + + void I2PServerTunnel::Accept() { + if (m_PortDestination) + m_PortDestination->SetAcceptor(std::bind(&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); + + auto localDestination = GetLocalDestination(); + if (localDestination) { + if (!localDestination->IsAcceptingStreams()) // set it as default if not set yet + localDestination->AcceptStreams( + std::bind(&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); + } else + LogPrint(eLogError, "I2PTunnel: Local destination not set for server tunnel"); + } + + void I2PServerTunnel::HandleAccept(std::shared_ptr stream) { + if (stream) { + if (m_IsAccessList) { + if (!m_AccessList.count(stream->GetRemoteIdentity()->GetIdentHash())) { + LogPrint(eLogWarning, "I2PTunnel: Address ", + stream->GetRemoteIdentity()->GetIdentHash().ToBase32(), + " is not in white list. Incoming connection dropped"); + stream->Close(); + return; + } + } + // new connection + auto conn = CreateI2PConnection(stream); + AddHandler(conn); + if (m_LocalAddress) + conn->Connect(*m_LocalAddress); + else + conn->Connect(m_IsUniqueLocal); + } + } + + std::shared_ptr + I2PServerTunnel::CreateI2PConnection(std::shared_ptr stream) { + return std::make_shared(this, stream, + std::make_shared(GetService()), + GetEndpoint()); + + } + + I2PServerTunnelHTTP::I2PServerTunnelHTTP(const std::string &name, const std::string &address, + int port, std::shared_ptr localDestination, + const std::string &host, int inport, bool gzip) : + I2PServerTunnel(name, address, port, localDestination, inport, gzip), + m_Host(host) { + } + + std::shared_ptr + I2PServerTunnelHTTP::CreateI2PConnection(std::shared_ptr stream) { + return std::make_shared(this, stream, + std::make_shared( + GetService()), GetEndpoint(), m_Host); + } + + I2PServerTunnelIRC::I2PServerTunnelIRC(const std::string &name, const std::string &address, + int port, std::shared_ptr localDestination, + const std::string &webircpass, int inport, bool gzip) : + I2PServerTunnel(name, address, port, localDestination, inport, gzip), + m_WebircPass(webircpass) { + } + + std::shared_ptr + I2PServerTunnelIRC::CreateI2PConnection(std::shared_ptr stream) { + return std::make_shared(this, stream, + std::make_shared( + GetService()), GetEndpoint(), this->m_WebircPass); + } + + void + I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len) { + if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash().GetLL()[0] || + fromPort != m_LastSession->RemotePort) { + std::lock_guard lock(m_SessionsMutex); + m_LastSession = ObtainUDPSession(from, toPort, fromPort); + } + m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); + m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); + } + + void I2PUDPServerTunnel::HandleRecvFromI2PRaw(uint16_t, uint16_t, const uint8_t *buf, size_t len) { + if (m_LastSession) { + m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); + m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); + } + } + + void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { + std::lock_guard lock(m_SessionsMutex); + uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); + auto itr = m_Sessions.begin(); + while (itr != m_Sessions.end()) { + if (now - (*itr)->LastActivity >= delta) + itr = m_Sessions.erase(itr); + else + ++itr; + } + } + + void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) { + std::lock_guard lock(m_SessionsMutex); + uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); + std::vector removePorts; + for (const auto &s: m_Sessions) { + if (now - s.second->second >= delta) + removePorts.push_back(s.first); + } + for (auto port: removePorts) { + m_Sessions.erase(port); + } + } + + UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx &from, uint16_t localPort, + uint16_t remotePort) { + auto ih = from.GetIdentHash(); + for (auto &s: m_Sessions) { + if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort) { + /** found existing session */ + LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); + return s; + } + } + boost::asio::ip::address addr; + /** create new udp session */ + if (m_IsUniqueLocal && m_LocalAddress.is_loopback()) { + auto ident = from.GetIdentHash(); + addr = GetLoopbackAddressFor(ident); + } else + addr = m_LocalAddress; + boost::asio::ip::udp::endpoint ep(addr, 0); + m_Sessions.push_back( + std::make_shared(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); + auto &back = m_Sessions.back(); + return back; + } + + UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, + const std::shared_ptr &localDestination, + boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash *to, + uint16_t ourPort, uint16_t theirPort) : + m_Destination(localDestination->GetDatagramDestination()), + IPSocket(localDestination->GetService(), localEndpoint), + SendEndpoint(endpoint), + LastActivity(i2p::util::GetMillisecondsSinceEpoch()), + LocalPort(ourPort), + RemotePort(theirPort) { + IPSocket.set_option(boost::asio::socket_base::receive_buffer_size(I2P_UDP_MAX_MTU)); + memcpy(Identity, to->data(), 32); + Receive(); + } + + void UDPSession::Receive() { + LogPrint(eLogDebug, "UDPSession: Receive"); + IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU), + FromEndpoint, + std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, + std::placeholders::_2)); + } + + void UDPSession::HandleReceived(const boost::system::error_code &ecode, std::size_t len) { + if (!ecode) { + LogPrint(eLogDebug, "UDPSession: Forward ", len, "B from ", FromEndpoint); + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + auto session = m_Destination->GetSession(Identity); + if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) + m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort); + else + m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort); + size_t numPackets = 0; + while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) { + boost::system::error_code ec; + size_t moreBytes = IPSocket.available(ec); + if (ec || !moreBytes) break; + len = IPSocket.receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec); + m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort); + numPackets++; + } + if (numPackets > 0) + LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint); + m_Destination->FlushSendQueue(session); + LastActivity = ts; + Receive(); + } else + LogPrint(eLogError, "UDPSession: ", ecode.message()); + } + + I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string &name, + std::shared_ptr localDestination, + boost::asio::ip::address localAddress, + boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) : + m_IsUniqueLocal(true), m_Name(name), m_LocalAddress(localAddress), + m_RemoteEndpoint(forwardTo), m_LocalDest(localDestination), m_Gzip(gzip) { + } + + I2PUDPServerTunnel::~I2PUDPServerTunnel() { + Stop(); + } + + void I2PUDPServerTunnel::Start() { + m_LocalDest->Start(); + + auto dgram = m_LocalDest->CreateDatagramDestination(m_Gzip); + dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, + std::placeholders::_5)); + dgram->SetRawReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + } + + void I2PUDPServerTunnel::Stop() { + auto dgram = m_LocalDest->GetDatagramDestination(); + if (dgram) dgram->ResetReceiver(); + } + + std::vector > I2PUDPServerTunnel::GetSessions() { + std::vector > sessions; + std::lock_guard lock(m_SessionsMutex); + + for (UDPSessionPtr s: m_Sessions) { + if (!s->m_Destination) continue; + auto info = s->m_Destination->GetInfoForRemote(s->Identity); + if (!info) continue; + + auto sinfo = std::make_shared(); + sinfo->Name = m_Name; + sinfo->LocalIdent = std::make_shared(m_LocalDest->GetIdentHash().data()); + sinfo->RemoteIdent = std::make_shared(s->Identity.data()); + sinfo->CurrentIBGW = info->IBGW; + sinfo->CurrentOBEP = info->OBEP; + sessions.push_back(sinfo); + } + return sessions; + } + + I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string &name, const std::string &remoteDest, + boost::asio::ip::udp::endpoint localEndpoint, + std::shared_ptr localDestination, + uint16_t remotePort, bool gzip) : + m_Name(name), m_RemoteDest(remoteDest), m_LocalDest(localDestination), m_LocalEndpoint(localEndpoint), + m_RemoteIdent(nullptr), m_ResolveThread(nullptr), m_LocalSocket(nullptr), RemotePort(remotePort), + m_LastPort(0), m_cancel_resolve(false), m_Gzip(gzip) { + } + + I2PUDPClientTunnel::~I2PUDPClientTunnel() { + Stop(); + } + + void I2PUDPClientTunnel::Start() { + // Reset flag in case of tunnel reload + if (m_cancel_resolve) m_cancel_resolve = false; + + m_LocalSocket.reset(new boost::asio::ip::udp::socket(m_LocalDest->GetService(), m_LocalEndpoint)); + m_LocalSocket->set_option(boost::asio::socket_base::receive_buffer_size(I2P_UDP_MAX_MTU)); + m_LocalSocket->set_option(boost::asio::socket_base::reuse_address(true)); + + auto dgram = m_LocalDest->CreateDatagramDestination(m_Gzip); + dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, + std::placeholders::_5)); + dgram->SetRawReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4)); + + m_LocalDest->Start(); + if (m_ResolveThread == nullptr) + m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this)); + RecvFromLocal(); + } + + void I2PUDPClientTunnel::Stop() { + auto dgram = m_LocalDest->GetDatagramDestination(); + if (dgram) dgram->ResetReceiver(); + m_cancel_resolve = true; + + m_Sessions.clear(); + + if (m_LocalSocket && m_LocalSocket->is_open()) + m_LocalSocket->close(); + + if (m_ResolveThread) { + m_ResolveThread->join(); + delete m_ResolveThread; + m_ResolveThread = nullptr; + } + if (m_RemoteIdent) { + delete m_RemoteIdent; + m_RemoteIdent = nullptr; + } + } + + void I2PUDPClientTunnel::RecvFromLocal() { + m_LocalSocket->async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU), + m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, + std::placeholders::_1, std::placeholders::_2)); + } + + void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code &ec, std::size_t transferred) { + if (m_cancel_resolve) { + LogPrint(eLogDebug, "UDP Client: Ignoring incomming data: stopping"); + return; + } + if (ec) { + LogPrint(eLogError, "UDP Client: Reading from socket error: ", ec.message(), + ". Restarting listener..."); + RecvFromLocal(); // Restart listener and continue work + return; + } + if (!m_RemoteIdent) { + LogPrint(eLogWarning, "UDP Client: Remote endpoint not resolved yet"); + RecvFromLocal(); + return; // drop, remote not resolved + } + auto remotePort = m_RecvEndpoint.port(); + if (!m_LastPort || m_LastPort != remotePort) { + auto itr = m_Sessions.find(remotePort); + if (itr != m_Sessions.end()) + m_LastSession = itr->second; + else { + m_LastSession = std::make_shared(boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0); + m_Sessions.emplace(remotePort, m_LastSession); + } + m_LastPort = remotePort; + } + // send off to remote i2p destination + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + LogPrint(eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort); + auto session = m_LocalDest->GetDatagramDestination()->GetSession(*m_RemoteIdent); + if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) + m_LocalDest->GetDatagramDestination()->SendDatagram(session, m_RecvBuff, transferred, remotePort, + RemotePort); + else + m_LocalDest->GetDatagramDestination()->SendRawDatagram(session, m_RecvBuff, transferred, remotePort, + RemotePort); + size_t numPackets = 0; + while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) { + boost::system::error_code ec; + size_t moreBytes = m_LocalSocket->available(ec); + if (ec || !moreBytes) break; + transferred = m_LocalSocket->receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU), + m_RecvEndpoint, 0, ec); + remotePort = m_RecvEndpoint.port(); + // TODO: check remotePort + m_LocalDest->GetDatagramDestination()->SendRawDatagram(session, m_RecvBuff, transferred, remotePort, + RemotePort); + numPackets++; + } + if (numPackets) + LogPrint(eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32()); + m_LocalDest->GetDatagramDestination()->FlushSendQueue(session); + + // mark convo as active + if (m_LastSession) + m_LastSession->second = ts; + RecvFromLocal(); + } + + std::vector > I2PUDPClientTunnel::GetSessions() { + // TODO: implement + std::vector > infos; + return infos; + } + + void I2PUDPClientTunnel::TryResolving() { + i2p::util::SetThreadName("UDP Resolver"); + LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); + + std::shared_ptr addr; + while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve) { + LogPrint(eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + if (m_cancel_resolve) { + LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled"); + return; + } + if (!addr || !addr->IsIdentHash()) { + LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found"); + return; + } + m_RemoteIdent = new i2p::data::IdentHash; + *m_RemoteIdent = addr->identHash; + LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); + } + + void + I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len) { + if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) + HandleRecvFromI2PRaw(fromPort, toPort, buf, len); + else + LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32()); + } + + void + I2PUDPClientTunnel::HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len) { + auto itr = m_Sessions.find(toPort); + // found convo ? + if (itr != m_Sessions.end()) { + // found convo + if (len > 0) { + LogPrint(eLogDebug, "UDP Client: Got ", len, "B from ", + m_RemoteIdent ? m_RemoteIdent->ToBase32() : ""); + m_LocalSocket->send_to(boost::asio::buffer(buf, len), itr->second->first); + // mark convo as active + itr->second->second = i2p::util::GetMillisecondsSinceEpoch(); + } + } else + LogPrint(eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort); + } + } } diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 90c5864e..3bf3216a 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -23,390 +23,425 @@ #include "I2PService.h" #include "AddressBook.h" -namespace i2p -{ -namespace client -{ - const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536; - const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds - const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds - // for HTTP tunnels - const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 - const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 - const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address - const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192; - - class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this - { - public: - - I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, int port = 0); // to I2P - I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr stream); // to I2P using simplified API - I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P - ~I2PTunnelConnection (); - void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); - void Connect (bool isUniqueLocal = true); - void Connect (const boost::asio::ip::address& localAddress); +namespace i2p { + namespace client { + const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536; + const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds + const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds + // for HTTP tunnels + const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 + const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 + const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address + const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192; - protected: + class I2PTunnelConnection : public I2PServiceHandler, public std::enable_shared_from_this { + public: - void Terminate (); + I2PTunnelConnection(I2PService *owner, std::shared_ptr socket, + std::shared_ptr leaseSet, int port = 0); // to I2P + I2PTunnelConnection(I2PService *owner, std::shared_ptr socket, + std::shared_ptr stream); // to I2P using simplified API + I2PTunnelConnection(I2PService *owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint &target, bool quiet = true); // from I2P + ~I2PTunnelConnection(); - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - virtual void Write (const uint8_t * buf, size_t len); // can be overloaded - void HandleWrite (const boost::system::error_code& ecode); - virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded + void I2PConnect(const uint8_t *msg = nullptr, size_t len = 0); - void StreamReceive (); - void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleConnect (const boost::system::error_code& ecode); + void Connect(bool isUniqueLocal = true); - std::shared_ptr GetSocket () const { return m_Socket; }; + void Connect(const boost::asio::ip::address &localAddress); - private: + protected: - uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; - std::shared_ptr m_Socket; - std::shared_ptr m_Stream; - boost::asio::ip::tcp::endpoint m_RemoteEndpoint; - bool m_IsQuiet; // don't send destination - }; + void Terminate(); - class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection - { - public: + void Receive(); - I2PClientTunnelConnectionHTTP (I2PService * owner, std::shared_ptr socket, - std::shared_ptr stream): - I2PTunnelConnection (owner, socket, stream), m_HeaderSent (false), - m_ConnectionSent (false), m_ProxyConnectionSent (false) {}; + void HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred); - protected: + virtual void Write(const uint8_t *buf, size_t len); // can be overloaded + void HandleWrite(const boost::system::error_code &ecode); - void Write (const uint8_t * buf, size_t len); + virtual void WriteToStream(const uint8_t *buf, size_t len); // can be overloaded - private: + void StreamReceive(); - std::stringstream m_InHeader, m_OutHeader; - bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent; - }; + void HandleStreamReceive(const boost::system::error_code &ecode, std::size_t bytes_transferred); - class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection - { - public: + void HandleConnect(const boost::system::error_code &ecode); - I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host); + std::shared_ptr GetSocket() const { return m_Socket; }; - protected: + private: - void Write (const uint8_t * buf, size_t len); - void WriteToStream (const uint8_t * buf, size_t len); + uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; + std::shared_ptr m_Socket; + std::shared_ptr m_Stream; + boost::asio::ip::tcp::endpoint m_RemoteEndpoint; + bool m_IsQuiet; // don't send destination + }; - private: + class I2PClientTunnelConnectionHTTP : public I2PTunnelConnection { + public: - std::string m_Host; - std::stringstream m_InHeader, m_OutHeader; - bool m_HeaderSent, m_ResponseHeaderSent; - std::shared_ptr m_From; - }; - - class I2PTunnelConnectionIRC: public I2PTunnelConnection - { - public: - - I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass); - - protected: - - void Write (const uint8_t * buf, size_t len); - - private: - - std::shared_ptr m_From; - std::stringstream m_OutPacket, m_InPacket; - bool m_NeedsWebIrc; - std::string m_WebircPass; - }; + I2PClientTunnelConnectionHTTP(I2PService *owner, std::shared_ptr socket, + std::shared_ptr stream) : + I2PTunnelConnection(owner, socket, stream), m_HeaderSent(false), + m_ConnectionSent(false), m_ProxyConnectionSent(false) {}; + protected: - class I2PClientTunnel: public TCPIPAcceptor - { - protected: + void Write(const uint8_t *buf, size_t len); - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); + private: - public: + std::stringstream m_InHeader, m_OutHeader; + bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent; + }; - I2PClientTunnel (const std::string& name, const std::string& destination, - const std::string& address, int port, std::shared_ptr localDestination, int destinationPort = 0); - ~I2PClientTunnel () {} + class I2PServerTunnelConnectionHTTP : public I2PTunnelConnection { + public: - void Start (); - void Stop (); + I2PServerTunnelConnectionHTTP(I2PService *owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint &target, const std::string &host); - const char* GetName() { return m_Name.c_str (); } - void SetKeepAliveInterval (uint32_t keepAliveInterval); + protected: - private: + void Write(const uint8_t *buf, size_t len); - std::shared_ptr GetAddress (); + void WriteToStream(const uint8_t *buf, size_t len); - void ScheduleKeepAliveTimer (); - void HandleKeepAliveTimer (const boost::system::error_code& ecode); + private: - private: + std::string m_Host; + std::stringstream m_InHeader, m_OutHeader; + bool m_HeaderSent, m_ResponseHeaderSent; + std::shared_ptr m_From; + }; - std::string m_Name, m_Destination; - std::shared_ptr m_Address; - int m_DestinationPort; - uint32_t m_KeepAliveInterval; - std::unique_ptr m_KeepAliveTimer; - }; + class I2PTunnelConnectionIRC : public I2PTunnelConnection { + public: + I2PTunnelConnectionIRC(I2PService *owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint &target, const std::string &m_WebircPass); - /** 2 minute timeout for udp sessions */ - const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; - const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds + protected: - /** max size for i2p udp */ - const size_t I2P_UDP_MAX_MTU = 64*1024; + void Write(const uint8_t *buf, size_t len); - struct UDPSession - { - i2p::datagram::DatagramDestination * m_Destination; - boost::asio::ip::udp::socket IPSocket; - i2p::data::IdentHash Identity; - boost::asio::ip::udp::endpoint FromEndpoint; - boost::asio::ip::udp::endpoint SendEndpoint; - uint64_t LastActivity; + private: - uint16_t LocalPort; - uint16_t RemotePort; - - uint8_t m_Buffer[I2P_UDP_MAX_MTU]; + std::shared_ptr m_From; + std::stringstream m_OutPacket, m_InPacket; + bool m_NeedsWebIrc; + std::string m_WebircPass; + }; - UDPSession(boost::asio::ip::udp::endpoint localEndpoint, - const std::shared_ptr & localDestination, - boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident, - uint16_t ourPort, uint16_t theirPort); - void HandleReceived(const boost::system::error_code & ecode, std::size_t len); - void Receive(); - }; + class I2PClientTunnel : public TCPIPAcceptor { + protected: - /** read only info about a datagram session */ - struct DatagramSessionInfo - { - /** the name of this forward */ - std::string Name; - /** ident hash of local destination */ - std::shared_ptr LocalIdent; - /** ident hash of remote destination */ - std::shared_ptr RemoteIdent; - /** ident hash of IBGW in use currently in this session or nullptr if none is set */ - std::shared_ptr CurrentIBGW; - /** ident hash of OBEP in use for this session or nullptr if none is set */ - std::shared_ptr CurrentOBEP; - /** i2p router's udp endpoint */ - boost::asio::ip::udp::endpoint LocalEndpoint; - /** client's udp endpoint */ - boost::asio::ip::udp::endpoint RemoteEndpoint; - /** how long has this converstation been idle in ms */ - uint64_t idle; - }; - - typedef std::shared_ptr UDPSessionPtr; - - /** server side udp tunnel, many i2p inbound to 1 ip outbound */ - class I2PUDPServerTunnel - { - public: - - I2PUDPServerTunnel (const std::string & name, - std::shared_ptr localDestination, - boost::asio::ip::address localAddress, - boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip); - ~I2PUDPServerTunnel (); - - /** expire stale udp conversations */ - void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); - void Start (); - void Stop (); - const char * GetName () const { return m_Name.c_str(); } - std::vector > GetSessions (); - std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - - void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } - - private: - - void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); - - private: - - bool m_IsUniqueLocal; - const std::string m_Name; - boost::asio::ip::address m_LocalAddress; - boost::asio::ip::udp::endpoint m_RemoteEndpoint; - std::mutex m_SessionsMutex; - std::vector m_Sessions; - std::shared_ptr m_LocalDest; - UDPSessionPtr m_LastSession; - bool m_Gzip; - - public: - - bool isUpdated; // transient, used during reload only - }; - - class I2PUDPClientTunnel - { - public: - - I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest, - boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr localDestination, - uint16_t remotePort, bool gzip); - ~I2PUDPClientTunnel (); - - void Start (); - void Stop (); - const char * GetName () const { return m_Name.c_str(); } - std::vector > GetSessions (); - - bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } - - std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - inline void SetLocalDestination (std::shared_ptr dest) - { - if (m_LocalDest) m_LocalDest->Release (); - if (dest) dest->Acquire (); - m_LocalDest = dest; - } - - void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); - - private: - - typedef std::pair UDPConvo; - void RecvFromLocal (); - void HandleRecvFromLocal (const boost::system::error_code & e, std::size_t transferred); - void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void TryResolving (); - - private: - - const std::string m_Name; - std::mutex m_SessionsMutex; - std::unordered_map > m_Sessions; // maps i2p port -> local udp convo - const std::string m_RemoteDest; - std::shared_ptr m_LocalDest; - const boost::asio::ip::udp::endpoint m_LocalEndpoint; - i2p::data::IdentHash * m_RemoteIdent; - std::thread * m_ResolveThread; - std::unique_ptr m_LocalSocket; - boost::asio::ip::udp::endpoint m_RecvEndpoint; - uint8_t m_RecvBuff[I2P_UDP_MAX_MTU]; - uint16_t RemotePort, m_LastPort; - bool m_cancel_resolve; - bool m_Gzip; - std::shared_ptr m_LastSession; + // Implements TCPIPAcceptor + std::shared_ptr CreateHandler(std::shared_ptr socket); - public: + public: - bool isUpdated; // transient, used during reload only - }; + I2PClientTunnel(const std::string &name, const std::string &destination, + const std::string &address, int port, std::shared_ptr localDestination, + int destinationPort = 0); - class I2PServerTunnel: public I2PService - { - public: + ~I2PClientTunnel() {} - I2PServerTunnel (const std::string& name, const std::string& address, int port, - std::shared_ptr localDestination, int inport = 0, bool gzip = true); + void Start(); - void Start (); - void Stop (); + void Stop(); - void SetAccessList (const std::set& accessList); + const char *GetName() { return m_Name.c_str(); } - void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } - bool IsUniqueLocal () const { return m_IsUniqueLocal; } + void SetKeepAliveInterval(uint32_t keepAliveInterval); - void SetLocalAddress (const std::string& localAddress); + private: - const std::string& GetAddress() const { return m_Address; } - int GetPort () const { return m_Port; }; - uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); }; - const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; } + std::shared_ptr GetAddress(); - const char* GetName() { return m_Name.c_str (); } + void ScheduleKeepAliveTimer(); - private: + void HandleKeepAliveTimer(const boost::system::error_code &ecode); - void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, - std::shared_ptr resolver); + private: - void Accept (); - void HandleAccept (std::shared_ptr stream); - virtual std::shared_ptr CreateI2PConnection (std::shared_ptr stream); + std::string m_Name, m_Destination; + std::shared_ptr m_Address; + int m_DestinationPort; + uint32_t m_KeepAliveInterval; + std::unique_ptr m_KeepAliveTimer; + }; - private: - bool m_IsUniqueLocal; - std::string m_Name, m_Address; - int m_Port; - boost::asio::ip::tcp::endpoint m_Endpoint; - std::shared_ptr m_PortDestination; - std::set m_AccessList; - bool m_IsAccessList; - std::unique_ptr m_LocalAddress; - }; + /** 2 minute timeout for udp sessions */ + const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; + const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds - class I2PServerTunnelHTTP: public I2PServerTunnel - { - public: + /** max size for i2p udp */ + const size_t I2P_UDP_MAX_MTU = 64 * 1024; - I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port, - std::shared_ptr localDestination, const std::string& host, - int inport = 0, bool gzip = true); + struct UDPSession { + i2p::datagram::DatagramDestination *m_Destination; + boost::asio::ip::udp::socket IPSocket; + i2p::data::IdentHash Identity; + boost::asio::ip::udp::endpoint FromEndpoint; + boost::asio::ip::udp::endpoint SendEndpoint; + uint64_t LastActivity; - private: + uint16_t LocalPort; + uint16_t RemotePort; - std::shared_ptr CreateI2PConnection (std::shared_ptr stream); + uint8_t m_Buffer[I2P_UDP_MAX_MTU]; - private: + UDPSession(boost::asio::ip::udp::endpoint localEndpoint, + const std::shared_ptr &localDestination, + boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash *ident, + uint16_t ourPort, uint16_t theirPort); - std::string m_Host; - }; + void HandleReceived(const boost::system::error_code &ecode, std::size_t len); - class I2PServerTunnelIRC: public I2PServerTunnel - { - public: + void Receive(); + }; - I2PServerTunnelIRC (const std::string& name, const std::string& address, int port, - std::shared_ptr localDestination, const std::string& webircpass, - int inport = 0, bool gzip = true); - private: + /** read only info about a datagram session */ + struct DatagramSessionInfo { + /** the name of this forward */ + std::string Name; + /** ident hash of local destination */ + std::shared_ptr LocalIdent; + /** ident hash of remote destination */ + std::shared_ptr RemoteIdent; + /** ident hash of IBGW in use currently in this session or nullptr if none is set */ + std::shared_ptr CurrentIBGW; + /** ident hash of OBEP in use for this session or nullptr if none is set */ + std::shared_ptr CurrentOBEP; + /** i2p router's udp endpoint */ + boost::asio::ip::udp::endpoint LocalEndpoint; + /** client's udp endpoint */ + boost::asio::ip::udp::endpoint RemoteEndpoint; + /** how long has this converstation been idle in ms */ + uint64_t idle; + }; - std::shared_ptr CreateI2PConnection (std::shared_ptr stream); + typedef std::shared_ptr UDPSessionPtr; - private: + /** server side udp tunnel, many i2p inbound to 1 ip outbound */ + class I2PUDPServerTunnel { + public: - std::string m_WebircPass; - }; -} + I2PUDPServerTunnel(const std::string &name, + std::shared_ptr localDestination, + boost::asio::ip::address localAddress, + boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip); + + ~I2PUDPServerTunnel(); + + /** expire stale udp conversations */ + void ExpireStale(const uint64_t delta = I2P_UDP_SESSION_TIMEOUT); + + void Start(); + + void Stop(); + + const char *GetName() const { return m_Name.c_str(); } + + std::vector > GetSessions(); + + std::shared_ptr GetLocalDestination() const { return m_LocalDest; } + + void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } + + private: + + void + HandleRecvFromI2P(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, const uint8_t *buf, + size_t len); + + void HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len); + + UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx &from, uint16_t localPort, uint16_t remotePort); + + private: + + bool m_IsUniqueLocal; + const std::string m_Name; + boost::asio::ip::address m_LocalAddress; + boost::asio::ip::udp::endpoint m_RemoteEndpoint; + std::mutex m_SessionsMutex; + std::vector m_Sessions; + std::shared_ptr m_LocalDest; + UDPSessionPtr m_LastSession; + bool m_Gzip; + + public: + + bool isUpdated; // transient, used during reload only + }; + + class I2PUDPClientTunnel { + public: + + I2PUDPClientTunnel(const std::string &name, const std::string &remoteDest, + boost::asio::ip::udp::endpoint localEndpoint, + std::shared_ptr localDestination, + uint16_t remotePort, bool gzip); + + ~I2PUDPClientTunnel(); + + void Start(); + + void Stop(); + + const char *GetName() const { return m_Name.c_str(); } + + std::vector > GetSessions(); + + bool IsLocalDestination(const i2p::data::IdentHash &destination) const { + return destination == m_LocalDest->GetIdentHash(); + } + + std::shared_ptr GetLocalDestination() const { return m_LocalDest; } + + inline void SetLocalDestination(std::shared_ptr dest) { + if (m_LocalDest) m_LocalDest->Release(); + if (dest) dest->Acquire(); + m_LocalDest = dest; + } + + void ExpireStale(const uint64_t delta = I2P_UDP_SESSION_TIMEOUT); + + private: + + typedef std::pair UDPConvo; + + void RecvFromLocal(); + + void HandleRecvFromLocal(const boost::system::error_code &e, std::size_t transferred); + + void + HandleRecvFromI2P(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, const uint8_t *buf, + size_t len); + + void HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len); + + void TryResolving(); + + private: + + const std::string m_Name; + std::mutex m_SessionsMutex; + std::unordered_map > m_Sessions; // maps i2p port -> local udp convo + const std::string m_RemoteDest; + std::shared_ptr m_LocalDest; + const boost::asio::ip::udp::endpoint m_LocalEndpoint; + i2p::data::IdentHash *m_RemoteIdent; + std::thread *m_ResolveThread; + std::unique_ptr m_LocalSocket; + boost::asio::ip::udp::endpoint m_RecvEndpoint; + uint8_t m_RecvBuff[I2P_UDP_MAX_MTU]; + uint16_t RemotePort, m_LastPort; + bool m_cancel_resolve; + bool m_Gzip; + std::shared_ptr m_LastSession; + + public: + + bool isUpdated; // transient, used during reload only + }; + + class I2PServerTunnel : public I2PService { + public: + + I2PServerTunnel(const std::string &name, const std::string &address, int port, + std::shared_ptr localDestination, int inport = 0, bool gzip = true); + + void Start(); + + void Stop(); + + void SetAccessList(const std::set &accessList); + + void SetUniqueLocal(bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } + + bool IsUniqueLocal() const { return m_IsUniqueLocal; } + + void SetLocalAddress(const std::string &localAddress); + + const std::string &GetAddress() const { return m_Address; } + + int GetPort() const { return m_Port; }; + + uint16_t GetLocalPort() const { return m_PortDestination->GetLocalPort(); }; + + const boost::asio::ip::tcp::endpoint &GetEndpoint() const { return m_Endpoint; } + + const char *GetName() { return m_Name.c_str(); } + + private: + + void HandleResolve(const boost::system::error_code &ecode, boost::asio::ip::tcp::resolver::iterator it, + std::shared_ptr resolver); + + void Accept(); + + void HandleAccept(std::shared_ptr stream); + + virtual std::shared_ptr + CreateI2PConnection(std::shared_ptr stream); + + private: + + bool m_IsUniqueLocal; + std::string m_Name, m_Address; + int m_Port; + boost::asio::ip::tcp::endpoint m_Endpoint; + std::shared_ptr m_PortDestination; + std::set m_AccessList; + bool m_IsAccessList; + std::unique_ptr m_LocalAddress; + }; + + class I2PServerTunnelHTTP : public I2PServerTunnel { + public: + + I2PServerTunnelHTTP(const std::string &name, const std::string &address, int port, + std::shared_ptr localDestination, const std::string &host, + int inport = 0, bool gzip = true); + + private: + + std::shared_ptr CreateI2PConnection(std::shared_ptr stream); + + private: + + std::string m_Host; + }; + + class I2PServerTunnelIRC : public I2PServerTunnel { + public: + + I2PServerTunnelIRC(const std::string &name, const std::string &address, int port, + std::shared_ptr localDestination, const std::string &webircpass, + int inport = 0, bool gzip = true); + + private: + + std::shared_ptr CreateI2PConnection(std::shared_ptr stream); + + private: + + std::string m_WebircPass; + }; + } } #endif diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index ce800ecc..5b3f10d3 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -11,97 +11,86 @@ #include "ClientContext.h" -namespace i2p -{ -namespace client -{ - MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys & keys, const std::string & remoteName, const std::map * params) - : RunnableClientDestination(keys, false, params), - m_RemoteName(remoteName) {} +namespace i2p { + namespace client { + MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys &keys, + const std::string &remoteName, + const std::map *params) + : RunnableClientDestination(keys, false, params), + m_RemoteName(remoteName) {} - void MatchedTunnelDestination::ResolveCurrentLeaseSet() - { - auto addr = i2p::client::context.GetAddressBook().GetAddress (m_RemoteName); - if(addr && addr->IsIdentHash ()) - { - m_RemoteIdent = addr->identHash; - auto ls = FindLeaseSet(m_RemoteIdent); - if(ls) - HandleFoundCurrentLeaseSet(ls); - else - RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1)); - } - else - LogPrint(eLogWarning, "Destination: Failed to resolve ", m_RemoteName); - } + void MatchedTunnelDestination::ResolveCurrentLeaseSet() { + auto addr = i2p::client::context.GetAddressBook().GetAddress(m_RemoteName); + if (addr && addr->IsIdentHash()) { + m_RemoteIdent = addr->identHash; + auto ls = FindLeaseSet(m_RemoteIdent); + if (ls) + HandleFoundCurrentLeaseSet(ls); + else + RequestDestination(m_RemoteIdent, + std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, + std::placeholders::_1)); + } else + LogPrint(eLogWarning, "Destination: Failed to resolve ", m_RemoteName); + } - void MatchedTunnelDestination::HandleFoundCurrentLeaseSet(std::shared_ptr ls) - { - if(ls) - { - LogPrint(eLogDebug, "Destination: Resolved remote lease set for ", m_RemoteName); - m_RemoteLeaseSet = ls; - } - else - { - m_ResolveTimer->expires_from_now(boost::posix_time::seconds(1)); - m_ResolveTimer->async_wait([&](const boost::system::error_code & ec) { - if(!ec) ResolveCurrentLeaseSet(); - }); - } - } + void MatchedTunnelDestination::HandleFoundCurrentLeaseSet(std::shared_ptr ls) { + if (ls) { + LogPrint(eLogDebug, "Destination: Resolved remote lease set for ", m_RemoteName); + m_RemoteLeaseSet = ls; + } else { + m_ResolveTimer->expires_from_now(boost::posix_time::seconds(1)); + m_ResolveTimer->async_wait([&](const boost::system::error_code &ec) { + if (!ec) ResolveCurrentLeaseSet(); + }); + } + } - void MatchedTunnelDestination::Start() - { - ClientDestination::Start(); - m_ResolveTimer = std::make_shared(GetService()); - GetTunnelPool()->SetCustomPeerSelector(this); - ResolveCurrentLeaseSet(); - } + void MatchedTunnelDestination::Start() { + ClientDestination::Start(); + m_ResolveTimer = std::make_shared(GetService()); + GetTunnelPool()->SetCustomPeerSelector(this); + ResolveCurrentLeaseSet(); + } - void MatchedTunnelDestination::Stop() - { - ClientDestination::Stop(); - if(m_ResolveTimer) - m_ResolveTimer->cancel(); - } + void MatchedTunnelDestination::Stop() { + ClientDestination::Stop(); + if (m_ResolveTimer) + m_ResolveTimer->cancel(); + } - bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound) - { - auto pool = GetTunnelPool(); - if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, - std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2))) - return false; - // more here for outbound tunnels - if(!inbound && m_RemoteLeaseSet) - { - if(m_RemoteLeaseSet->IsExpired()) - ResolveCurrentLeaseSet(); - if(m_RemoteLeaseSet && !m_RemoteLeaseSet->IsExpired()) - { - // remote lease set is good - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases(); - // pick lease - std::shared_ptr obep; - while(!obep && leases.size() > 0) - { - auto idx = rand() % leases.size(); - auto lease = leases[idx]; - obep = i2p::data::netdb.FindRouter(lease->tunnelGateway); - leases.erase(leases.begin()+idx); - } - if(obep) - { - path.Add (obep); - LogPrint(eLogDebug, "Destination: Found OBEP matching IBGW"); - } else - LogPrint(eLogWarning, "Destination: Could not find proper IBGW for matched outbound tunnel"); - } - } - return true; - } -} + bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path &path, int hops, bool inbound) { + auto pool = GetTunnelPool(); + if (!i2p::tunnel::StandardSelectPeers(path, hops, inbound, + std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, + std::placeholders::_1, std::placeholders::_2))) + return false; + // more here for outbound tunnels + if (!inbound && m_RemoteLeaseSet) { + if (m_RemoteLeaseSet->IsExpired()) + ResolveCurrentLeaseSet(); + if (m_RemoteLeaseSet && !m_RemoteLeaseSet->IsExpired()) { + // remote lease set is good + auto leases = m_RemoteLeaseSet->GetNonExpiredLeases(); + // pick lease + std::shared_ptr obep; + while (!obep && leases.size() > 0) { + auto idx = rand() % leases.size(); + auto lease = leases[idx]; + obep = i2p::data::netdb.FindRouter(lease->tunnelGateway); + leases.erase(leases.begin() + idx); + } + if (obep) { + path.Add(obep); + LogPrint(eLogDebug, "Destination: Found OBEP matching IBGW"); + } else + LogPrint(eLogWarning, "Destination: Could not find proper IBGW for matched outbound tunnel"); + } + } + return true; + } + } } diff --git a/libi2pd_client/MatchedDestination.h b/libi2pd_client/MatchedDestination.h index 30ad8942..326a7bf5 100644 --- a/libi2pd_client/MatchedDestination.h +++ b/libi2pd_client/MatchedDestination.h @@ -8,40 +8,41 @@ #ifndef MATCHED_DESTINATION_H_ #define MATCHED_DESTINATION_H_ + #include "Destination.h" #include -namespace i2p -{ -namespace client -{ - /** - * client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination - */ - class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector - { - public: +namespace i2p { + namespace client { + /** + * client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination + */ + class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector { + public: - MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, - const std::map * params = nullptr); - void Start(); - void Stop(); + MatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string &remoteName, + const std::map *params = nullptr); - bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); + void Start(); - private: + void Stop(); - void ResolveCurrentLeaseSet(); - void HandleFoundCurrentLeaseSet(std::shared_ptr ls); + bool SelectPeers(i2p::tunnel::Path &peers, int hops, bool inbound); - private: + private: - std::string m_RemoteName; - i2p::data::IdentHash m_RemoteIdent; - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_ResolveTimer; - }; -} + void ResolveCurrentLeaseSet(); + + void HandleFoundCurrentLeaseSet(std::shared_ptr ls); + + private: + + std::string m_RemoteName; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_ResolveTimer; + }; + } } #endif diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 5d0f4425..1a5774b1 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -8,9 +8,11 @@ #include #include + #ifdef _MSC_VER #include #endif + #include "Base.h" #include "Identity.h" #include "Log.h" @@ -19,1519 +21,1295 @@ #include "util.h" #include "SAM.h" -namespace i2p -{ -namespace client -{ - SAMSocket::SAMSocket (SAMBridge& owner): - m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()), - m_BufferOffset (0), - m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), - m_IsAccepting (false), m_Stream (nullptr) - { - } +namespace i2p { + namespace client { + SAMSocket::SAMSocket(SAMBridge &owner) : + m_Owner(owner), m_Socket(owner.GetService()), m_Timer(m_Owner.GetService()), + m_BufferOffset(0), + m_SocketType(eSAMSocketTypeUnknown), m_IsSilent(false), + m_IsAccepting(false), m_Stream(nullptr) { + } - SAMSocket::~SAMSocket () - { - m_Stream = nullptr; - } + SAMSocket::~SAMSocket() { + m_Stream = nullptr; + } - void SAMSocket::Terminate (const char* reason) - { - if(m_Stream) - { - m_Stream->AsyncClose (); - m_Stream = nullptr; - } - auto Session = m_Owner.FindSession(m_ID); - switch (m_SocketType) - { - case eSAMSocketTypeSession: - m_Owner.CloseSession (m_ID); - break; - case eSAMSocketTypeStream: - { - break; - } - case eSAMSocketTypeAcceptor: - case eSAMSocketTypeForward: - { - if (Session) - { - if (m_IsAccepting && Session->GetLocalDestination ()) - Session->GetLocalDestination ()->StopAcceptingStreams (); - } - break; - } - default: ; - } - m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket.is_open ()) - { - boost::system::error_code ec; - m_Socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); - m_Socket.close (); - } - m_Owner.RemoveSocket(shared_from_this()); - } + void SAMSocket::Terminate(const char *reason) { + if (m_Stream) { + m_Stream->AsyncClose(); + m_Stream = nullptr; + } + auto Session = m_Owner.FindSession(m_ID); + switch (m_SocketType) { + case eSAMSocketTypeSession: + m_Owner.CloseSession(m_ID); + break; + case eSAMSocketTypeStream: { + break; + } + case eSAMSocketTypeAcceptor: + case eSAMSocketTypeForward: { + if (Session) { + if (m_IsAccepting && Session->GetLocalDestination()) + Session->GetLocalDestination()->StopAcceptingStreams(); + } + break; + } + default:; + } + m_SocketType = eSAMSocketTypeTerminated; + if (m_Socket.is_open()) { + boost::system::error_code ec; + m_Socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + m_Socket.close(); + } + m_Owner.RemoveSocket(shared_from_this()); + } - void SAMSocket::ReceiveHandshake () - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } + void SAMSocket::ReceiveHandshake() { + m_Socket.async_read_some(boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - static bool SAMVersionAcceptable(const std::string & ver) - { - return ver == "3.0" || ver == "3.1"; - } + static bool SAMVersionAcceptable(const std::string &ver) { + return ver == "3.0" || ver == "3.1"; + } - static bool SAMVersionTooLow(const std::string & ver) - { - return ver.size() && ver[0] < '3'; - } + static bool SAMVersionTooLow(const std::string &ver) { + return ver.size() && ver[0] < '3'; + } - static bool SAMVersionTooHigh(const std::string & ver) - { - return ver.size() && ver > "3.1"; - } + static bool SAMVersionTooHigh(const std::string &ver) { + return ver.size() && ver > "3.1"; + } - void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Handshake read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: handshake read error"); - } - else - { - m_Buffer[bytes_transferred] = 0; - char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); - if (eol) - *eol = 0; - LogPrint (eLogDebug, "SAM: Handshake ", m_Buffer); - char * separator = strchr (m_Buffer, ' '); - if (separator) - { - separator = strchr (separator + 1, ' '); - if (separator) - *separator = 0; - } + void SAMSocket::HandleHandshakeReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "SAM: Handshake read error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate("SAM: handshake read error"); + } else { + m_Buffer[bytes_transferred] = 0; + char *eol = (char *) memchr(m_Buffer, '\n', bytes_transferred); + if (eol) + *eol = 0; + LogPrint(eLogDebug, "SAM: Handshake ", m_Buffer); + char *separator = strchr(m_Buffer, ' '); + if (separator) { + separator = strchr(separator + 1, ' '); + if (separator) + *separator = 0; + } - if (!strcmp (m_Buffer, SAM_HANDSHAKE)) - { - std::string maxver("3.1"); - std::string minver("3.0"); - // try to find MIN and MAX, 3.0 if not found - if (separator) - { - separator++; - std::map params; - ExtractParams (separator, params); - auto it = params.find (SAM_PARAM_MAX); - if (it != params.end ()) - maxver = it->second; - it = params.find(SAM_PARAM_MIN); - if (it != params.end ()) - minver = it->second; - } - // version negotiation - std::string version; - if (SAMVersionAcceptable(maxver)) - { - version = maxver; - } - else if (SAMVersionAcceptable(minver)) - { - version = minver; - } - else if (SAMVersionTooLow(minver) && SAMVersionTooHigh(maxver)) - { - version = "3.0"; - } + if (!strcmp(m_Buffer, SAM_HANDSHAKE)) { + std::string maxver("3.1"); + std::string minver("3.0"); + // try to find MIN and MAX, 3.0 if not found + if (separator) { + separator++; + std::map params; + ExtractParams(separator, params); + auto it = params.find(SAM_PARAM_MAX); + if (it != params.end()) + maxver = it->second; + it = params.find(SAM_PARAM_MIN); + if (it != params.end()) + minver = it->second; + } + // version negotiation + std::string version; + if (SAMVersionAcceptable(maxver)) { + version = maxver; + } else if (SAMVersionAcceptable(minver)) { + version = minver; + } else if (SAMVersionTooLow(minver) && SAMVersionTooHigh(maxver)) { + version = "3.0"; + } - if (SAMVersionAcceptable(version)) - { + if (SAMVersionAcceptable(version)) { #ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); #else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); + size_t l = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str()); #endif - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - else - SendMessageReply (SAM_HANDSHAKE_NOVERSION, strlen (SAM_HANDSHAKE_NOVERSION), true); - } - else - { - LogPrint (eLogError, "SAM: Handshake mismatch"); - Terminate ("SAM: handshake mismatch"); - } - } - } + boost::asio::async_write(m_Socket, boost::asio::buffer(m_Buffer, l), + boost::asio::transfer_all(), + std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } else + SendMessageReply(SAM_HANDSHAKE_NOVERSION, strlen(SAM_HANDSHAKE_NOVERSION), true); + } else { + LogPrint(eLogError, "SAM: Handshake mismatch"); + Terminate("SAM: handshake mismatch"); + } + } + } - bool SAMSocket::IsSession(const std::string & id) const - { - return id == m_ID; - } + bool SAMSocket::IsSession(const std::string &id) const { + return id == m_ID; + } - void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Handshake reply send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: handshake reply send error"); - } - else - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleMessage, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - } + void + SAMSocket::HandleHandshakeReplySent(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "SAM: Handshake reply send error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate("SAM: handshake reply send error"); + } else { + m_Socket.async_read_some(boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleMessage, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + } - void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) - { - LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg); + void SAMSocket::SendMessageReply(const char *msg, size_t len, bool close) { + LogPrint(eLogDebug, "SAMSocket::SendMessageReply, close=", close ? "true" : "false", " reason: ", msg); - if (!m_IsSilent || m_SocketType == eSAMSocketTypeForward) - boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, close)); - else - { - if (close) - Terminate ("SAMSocket::SendMessageReply(close=true)"); - else - Receive (); - } - } + if (!m_IsSilent || m_SocketType == eSAMSocketTypeForward) + boost::asio::async_write(m_Socket, boost::asio::buffer(msg, len), boost::asio::transfer_all(), + std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this(), + std::placeholders::_1, std::placeholders::_2, close)); + else { + if (close) + Terminate("SAMSocket::SendMessageReply(close=true)"); + else + Receive(); + } + } - void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Reply send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: reply send error"); - } - else - { - if (close) - Terminate ("SAMSocket::HandleMessageReplySent(close=true)"); - else - Receive (); - } - } + void SAMSocket::HandleMessageReplySent(const boost::system::error_code &ecode, std::size_t bytes_transferred, + bool close) { + if (ecode) { + LogPrint(eLogError, "SAM: Reply send error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate("SAM: reply send error"); + } else { + if (close) + Terminate("SAMSocket::HandleMessageReplySent(close=true)"); + else + Receive(); + } + } - void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: read error"); - } - else if (m_SocketType == eSAMSocketTypeStream) - HandleReceived (ecode, bytes_transferred); - else - { - bytes_transferred += m_BufferOffset; - m_BufferOffset = 0; - m_Buffer[bytes_transferred] = 0; - char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); - if (eol) - { - if (eol > m_Buffer && eol[-1] == '\r') eol--; - *eol = 0; - char * separator = strchr (m_Buffer, ' '); - if (separator) - { - separator = strchr (separator + 1, ' '); - if (separator) - *separator = 0; - else - separator = eol; + void SAMSocket::HandleMessage(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "SAM: Read error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate("SAM: read error"); + } else if (m_SocketType == eSAMSocketTypeStream) + HandleReceived(ecode, bytes_transferred); + else { + bytes_transferred += m_BufferOffset; + m_BufferOffset = 0; + m_Buffer[bytes_transferred] = 0; + char *eol = (char *) memchr(m_Buffer, '\n', bytes_transferred); + if (eol) { + if (eol > m_Buffer && eol[-1] == '\r') eol--; + *eol = 0; + char *separator = strchr(m_Buffer, ' '); + if (separator) { + separator = strchr(separator + 1, ' '); + if (separator) + *separator = 0; + else + separator = eol; - if (!strcmp (m_Buffer, SAM_SESSION_CREATE)) - ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT)) - ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1, bytes_transferred - (eol - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT)) - ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_FORWARD)) - ProcessStreamForward (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_DEST_GENERATE)) - ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) - ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_SESSION_ADD)) - ProcessSessionAdd (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_SESSION_REMOVE)) - ProcessSessionRemove (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND)) - { - size_t len = bytes_transferred - (separator - m_Buffer) - 1; - size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1); - if (processed < len) - { - m_BufferOffset = len - processed; - if (processed > 0) - memmove (m_Buffer, separator + 1 + processed, m_BufferOffset); - else - { - // restore string back - *separator = ' '; - *eol = '\n'; - } - } - // since it's SAM v1 reply is not expected - Receive (); - } - else - { - LogPrint (eLogError, "SAM: Unexpected message ", m_Buffer); - Terminate ("SAM: unexpected message"); - } - } - else - { - LogPrint (eLogError, "SAM: Malformed message ", m_Buffer); - Terminate ("malformed message"); - } - } + if (!strcmp(m_Buffer, SAM_SESSION_CREATE)) + ProcessSessionCreate(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_STREAM_CONNECT)) + ProcessStreamConnect(separator + 1, bytes_transferred - (separator - m_Buffer) - 1, + bytes_transferred - (eol - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_STREAM_ACCEPT)) + ProcessStreamAccept(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_STREAM_FORWARD)) + ProcessStreamForward(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_DEST_GENERATE)) + ProcessDestGenerate(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_NAMING_LOOKUP)) + ProcessNamingLookup(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_SESSION_ADD)) + ProcessSessionAdd(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_SESSION_REMOVE)) + ProcessSessionRemove(separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp(m_Buffer, SAM_DATAGRAM_SEND) || !strcmp(m_Buffer, SAM_RAW_SEND)) { + size_t len = bytes_transferred - (separator - m_Buffer) - 1; + size_t processed = ProcessDatagramSend(separator + 1, len, eol + 1); + if (processed < len) { + m_BufferOffset = len - processed; + if (processed > 0) + memmove(m_Buffer, separator + 1 + processed, m_BufferOffset); + else { + // restore string back + *separator = ' '; + *eol = '\n'; + } + } + // since it's SAM v1 reply is not expected + Receive(); + } else { + LogPrint(eLogError, "SAM: Unexpected message ", m_Buffer); + Terminate("SAM: unexpected message"); + } + } else { + LogPrint(eLogError, "SAM: Malformed message ", m_Buffer); + Terminate("malformed message"); + } + } else { + LogPrint(eLogWarning, "SAM: Incomplete message ", bytes_transferred); + m_BufferOffset = bytes_transferred; + // try to receive remaining message + Receive(); + } + } + } - else - { - LogPrint (eLogWarning, "SAM: Incomplete message ", bytes_transferred); - m_BufferOffset = bytes_transferred; - // try to receive remaining message - Receive (); - } - } - } + static bool IsAcceptableSessionName(const std::string &str) { + auto itr = str.begin(); + while (itr != str.end()) { + char ch = *itr; + ++itr; + if (ch == '<' || ch == '>' || ch == '"' || ch == '\'' || ch == '/') + return false; + } + return true; + } - static bool IsAcceptableSessionName(const std::string & str) - { - auto itr = str.begin(); - while(itr != str.end()) - { - char ch = *itr; - ++itr; - if (ch == '<' || ch == '>' || ch == '"' || ch == '\'' || ch == '/') - return false; - } - return true; - } + void SAMSocket::ProcessSessionCreate(char *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Session create: ", buf); + std::map params; + ExtractParams(buf, params); + std::string &style = params[SAM_PARAM_STYLE]; + std::string &id = params[SAM_PARAM_ID]; + std::string &destination = params[SAM_PARAM_DESTINATION]; - void SAMSocket::ProcessSessionCreate (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Session create: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& style = params[SAM_PARAM_STYLE]; - std::string& id = params[SAM_PARAM_ID]; - std::string& destination = params[SAM_PARAM_DESTINATION]; + if (!IsAcceptableSessionName(id)) { + // invalid session id + SendMessageReply(SAM_SESSION_CREATE_INVALID_ID, strlen(SAM_SESSION_CREATE_INVALID_ID), true); + return; + } + m_ID = id; + if (m_Owner.FindSession(id)) { + // session exists + SendMessageReply(SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); + return; + } - if(!IsAcceptableSessionName(id)) - { - // invalid session id - SendMessageReply (SAM_SESSION_CREATE_INVALID_ID, strlen(SAM_SESSION_CREATE_INVALID_ID), true); - return; - } - m_ID = id; - if (m_Owner.FindSession (id)) - { - // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); - return; - } + SAMSessionType type = eSAMSessionTypeUnknown; + if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; + else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram; + else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw; + else if (style == SAM_VALUE_MASTER) type = eSAMSessionTypeMaster; + if (type == eSAMSessionTypeUnknown) { + // unknown style + SendI2PError("Unknown STYLE"); + return; + } - SAMSessionType type = eSAMSessionTypeUnknown; - if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; - else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram; - else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw; - else if (style == SAM_VALUE_MASTER) type = eSAMSessionTypeMaster; - if (type == eSAMSessionTypeUnknown) - { - // unknown style - SendI2PError("Unknown STYLE"); - return; - } + std::shared_ptr forward = nullptr; + if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) && + params.find(SAM_PARAM_HOST) != params.end() && params.find(SAM_PARAM_PORT) != params.end()) { + // udp forward selected + boost::system::error_code e; + // TODO: support hostnames in udp forward + auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e); + if (e) { + // not an ip address + SendI2PError("Invalid IP Address in HOST"); + return; + } - std::shared_ptr forward = nullptr; - if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) && - params.find(SAM_PARAM_HOST) != params.end() && params.find(SAM_PARAM_PORT) != params.end()) - { - // udp forward selected - boost::system::error_code e; - // TODO: support hostnames in udp forward - auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e); - if (e) - { - // not an ip address - SendI2PError("Invalid IP Address in HOST"); - return; - } + auto port = std::stoi(params[SAM_PARAM_PORT]); + if (port == -1) { + SendI2PError("Invalid port"); + return; + } + forward = std::make_shared(addr, port); + } - auto port = std::stoi(params[SAM_PARAM_PORT]); - if (port == -1) - { - SendI2PError("Invalid port"); - return; - } - forward = std::make_shared(addr, port); - } + //ensure we actually received a destination + if (destination.empty()) { + SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); + return; + } - //ensure we actually received a destination - if (destination.empty()) - { - SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); - return; - } + if (destination != SAM_VALUE_TRANSIENT) { + //ensure it's a base64 string + i2p::data::PrivateKeys keys; + if (!keys.FromBase64(destination)) { + SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); + return; + } + } - if (destination != SAM_VALUE_TRANSIENT) - { - //ensure it's a base64 string - i2p::data::PrivateKeys keys; - if (!keys.FromBase64(destination)) - { - SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); - return; - } - } + // create destination + auto session = m_Owner.CreateSession(id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, + ¶ms); + if (session) { + m_SocketType = eSAMSocketTypeSession; + if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) { + session->UDPEndpoint = forward; + auto dest = session->GetLocalDestination()->CreateDatagramDestination(); + if (type == eSAMSessionTypeDatagram) + dest->SetReceiver(std::bind(&SAMSocket::HandleI2PDatagramReceive, shared_from_this(), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5)); + else // raw + dest->SetRawReceiver(std::bind(&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this(), + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4)); + } - // create destination - auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); - if (session) - { - m_SocketType = eSAMSocketTypeSession; - if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) - { - session->UDPEndpoint = forward; - auto dest = session->GetLocalDestination ()->CreateDatagramDestination (); - if (type == eSAMSessionTypeDatagram) - dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - else // raw - dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - } + if (session->GetLocalDestination()->IsReady()) + SendSessionCreateReplyOk(); + else { + m_Timer.expires_from_now(boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); + m_Timer.async_wait(std::bind(&SAMSocket::HandleSessionReadinessCheckTimer, + shared_from_this(), std::placeholders::_1)); + } + } else + SendMessageReply(SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); + } - if (session->GetLocalDestination ()->IsReady ()) - SendSessionCreateReplyOk (); - else - { - m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); - m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); - } - } - else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); - } + void SAMSocket::HandleSessionReadinessCheckTimer(const boost::system::error_code &ecode) { + if (ecode != boost::asio::error::operation_aborted) { + auto session = m_Owner.FindSession(m_ID); + if (session) { + if (session->GetLocalDestination()->IsReady()) + SendSessionCreateReplyOk(); + else { + m_Timer.expires_from_now(boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); + m_Timer.async_wait(std::bind(&SAMSocket::HandleSessionReadinessCheckTimer, + shared_from_this(), std::placeholders::_1)); + } + } + } + } - void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto session = m_Owner.FindSession(m_ID); - if(session) - { - if (session->GetLocalDestination ()->IsReady ()) - SendSessionCreateReplyOk (); - else - { - m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); - m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); - } - } - } - } - - void SAMSocket::SendSessionCreateReplyOk () - { - auto session = m_Owner.FindSession(m_ID); - if (session) - { - uint8_t buf[1024]; - char priv[1024]; - size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024); - size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024); - priv[l1] = 0; + void SAMSocket::SendSessionCreateReplyOk() { + auto session = m_Owner.FindSession(m_ID); + if (session) { + uint8_t buf[1024]; + char priv[1024]; + size_t l = session->GetLocalDestination()->GetPrivateKeys().ToBuffer(buf, 1024); + size_t l1 = i2p::data::ByteStreamToBase64(buf, l, priv, 1024); + priv[l1] = 0; #ifdef _MSC_VER - size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); + size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); #else - size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); + size_t l2 = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); #endif - SendMessageReply (m_Buffer, l2, false); - } - } + SendMessageReply(m_Buffer, l2, false); + } + } - void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem) - { - LogPrint (eLogDebug, "SAM: Stream connect: ", buf); - if ( m_SocketType != eSAMSocketTypeUnknown) - { - SendI2PError ("Socket already in use"); - return; - } - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - std::string& destination = params[SAM_PARAM_DESTINATION]; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - m_ID = id; - auto session = m_Owner.FindSession (id); - if (session) - { - if (rem > 0) // handle follow on data - { - memmove (m_Buffer, buf + len + 1, rem); // buf is a pointer to m_Buffer's content - m_BufferOffset = rem; - } - else - m_BufferOffset = 0; + void SAMSocket::ProcessStreamConnect(char *buf, size_t len, size_t rem) { + LogPrint(eLogDebug, "SAM: Stream connect: ", buf); + if (m_SocketType != eSAMSocketTypeUnknown) { + SendI2PError("Socket already in use"); + return; + } + std::map params; + ExtractParams(buf, params); + std::string &id = params[SAM_PARAM_ID]; + std::string &destination = params[SAM_PARAM_DESTINATION]; + std::string &silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + m_ID = id; + auto session = m_Owner.FindSession(id); + if (session) { + if (rem > 0) // handle follow on data + { + memmove(m_Buffer, buf + len + 1, rem); // buf is a pointer to m_Buffer's content + m_BufferOffset = rem; + } else + m_BufferOffset = 0; - std::shared_ptr addr; - if (destination.find(".i2p") != std::string::npos) - addr = context.GetAddressBook().GetAddress (destination); - else - { - auto dest = std::make_shared (); - size_t l = dest->FromBase64(destination); - if (l > 0) - { - context.GetAddressBook().InsertFullAddress(dest); - addr = std::make_shared
(dest->GetIdentHash ()); - } - } + std::shared_ptr addr; + if (destination.find(".i2p") != std::string::npos) + addr = context.GetAddressBook().GetAddress(destination); + else { + auto dest = std::make_shared(); + size_t l = dest->FromBase64(destination); + if (l > 0) { + context.GetAddressBook().InsertFullAddress(dest); + addr = std::make_shared
(dest->GetIdentHash()); + } + } - if (addr && addr->IsValid ()) - { - if (addr->IsIdentHash ()) - { - auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash); - if (leaseSet) - Connect(leaseSet, session); - else - { - session->GetLocalDestination ()->RequestDestination(addr->identHash, - std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, - shared_from_this(), std::placeholders::_1)); - } - } - else // B33 - session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, - std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, - shared_from_this(), std::placeholders::_1)); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } + if (addr && addr->IsValid()) { + if (addr->IsIdentHash()) { + auto leaseSet = session->GetLocalDestination()->FindLeaseSet(addr->identHash); + if (leaseSet) + Connect(leaseSet, session); + else { + session->GetLocalDestination()->RequestDestination(addr->identHash, + std::bind( + &SAMSocket::HandleConnectLeaseSetRequestComplete, + shared_from_this(), + std::placeholders::_1)); + } + } else // B33 + session->GetLocalDestination()->RequestDestinationWithEncryptedLeaseSet(addr->blindedPublicKey, + std::bind( + &SAMSocket::HandleConnectLeaseSetRequestComplete, + shared_from_this(), + std::placeholders::_1)); + } else + SendMessageReply(SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true); + } else + SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } - void SAMSocket::Connect (std::shared_ptr remote, std::shared_ptr session) - { - if (!session) session = m_Owner.FindSession(m_ID); - if (session) - { - m_SocketType = eSAMSocketTypeStream; - m_Stream = session->GetLocalDestination ()->CreateStream (remote); - if (m_Stream) - { - m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send - m_BufferOffset = 0; - I2PReceive (); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } + void + SAMSocket::Connect(std::shared_ptr remote, std::shared_ptr session) { + if (!session) session = m_Owner.FindSession(m_ID); + if (session) { + m_SocketType = eSAMSocketTypeStream; + m_Stream = session->GetLocalDestination()->CreateStream(remote); + if (m_Stream) { + m_Stream->Send((uint8_t *) m_Buffer, m_BufferOffset); // connect and send + m_BufferOffset = 0; + I2PReceive(); + SendMessageReply(SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } else + SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } else + SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } - void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet) - { - if (leaseSet) - Connect (leaseSet); - else - { - LogPrint (eLogError, "SAM: Destination to connect not found"); - SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); - } - } + void SAMSocket::HandleConnectLeaseSetRequestComplete(std::shared_ptr leaseSet) { + if (leaseSet) + Connect(leaseSet); + else { + LogPrint(eLogError, "SAM: Destination to connect not found"); + SendMessageReply(SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); + } + } - void SAMSocket::ProcessStreamAccept (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Stream accept: ", buf); - if ( m_SocketType != eSAMSocketTypeUnknown) - { - SendI2PError ("Socket already in use"); - return; - } - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - m_ID = id; - auto session = m_Owner.FindSession (id); - if (session) - { - m_SocketType = eSAMSocketTypeAcceptor; - if (!session->GetLocalDestination ()->IsAcceptingStreams ()) - { - m_IsAccepting = true; - session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); - } - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } + void SAMSocket::ProcessStreamAccept(char *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Stream accept: ", buf); + if (m_SocketType != eSAMSocketTypeUnknown) { + SendI2PError("Socket already in use"); + return; + } + std::map params; + ExtractParams(buf, params); + std::string &id = params[SAM_PARAM_ID]; + std::string &silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + m_ID = id; + auto session = m_Owner.FindSession(id); + if (session) { + m_SocketType = eSAMSocketTypeAcceptor; + if (!session->GetLocalDestination()->IsAcceptingStreams()) { + m_IsAccepting = true; + session->GetLocalDestination()->AcceptOnce( + std::bind(&SAMSocket::HandleI2PAccept, shared_from_this(), std::placeholders::_1)); + } + SendMessageReply(SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } else + SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } - void SAMSocket::ProcessStreamForward (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Stream forward: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - auto session = m_Owner.FindSession (id); - if (!session) - { - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - return; - } - if (session->GetLocalDestination ()->IsAcceptingStreams ()) - { - SendI2PError ("Already accepting"); - return; - } - auto it = params.find (SAM_PARAM_PORT); - if (it == params.end ()) - { - SendI2PError ("PORT is missing"); - return; - } - auto port = std::stoi (it->second); - if (port <= 0 || port >= 0xFFFF) - { - SendI2PError ("Invalid PORT"); - return; - } - boost::system::error_code ec; - auto ep = m_Socket.remote_endpoint (ec); - if (ec) - { - SendI2PError ("Socket error"); - return; - } - ep.port (port); - m_SocketType = eSAMSocketTypeForward; - m_ID = id; - m_IsAccepting = true; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - session->GetLocalDestination ()->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward, - shared_from_this (), std::placeholders::_1, ep)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } + void SAMSocket::ProcessStreamForward(char *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Stream forward: ", buf); + std::map params; + ExtractParams(buf, params); + std::string &id = params[SAM_PARAM_ID]; + auto session = m_Owner.FindSession(id); + if (!session) { + SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + return; + } + if (session->GetLocalDestination()->IsAcceptingStreams()) { + SendI2PError("Already accepting"); + return; + } + auto it = params.find(SAM_PARAM_PORT); + if (it == params.end()) { + SendI2PError("PORT is missing"); + return; + } + auto port = std::stoi(it->second); + if (port <= 0 || port >= 0xFFFF) { + SendI2PError("Invalid PORT"); + return; + } + boost::system::error_code ec; + auto ep = m_Socket.remote_endpoint(ec); + if (ec) { + SendI2PError("Socket error"); + return; + } + ep.port(port); + m_SocketType = eSAMSocketTypeForward; + m_ID = id; + m_IsAccepting = true; + std::string &silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + session->GetLocalDestination()->AcceptStreams(std::bind(&SAMSocket::HandleI2PForward, + shared_from_this(), std::placeholders::_1, ep)); + SendMessageReply(SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } - size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) - { - LogPrint (eLogDebug, "SAM: Datagram send: ", buf, " ", len); - std::map params; - ExtractParams (buf, params); - size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf; - if (offset + size <= len) - { - auto session = m_Owner.FindSession(m_ID); - if (session) - { - auto d = session->GetLocalDestination ()->GetDatagramDestination (); - if (d) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (params[SAM_PARAM_DESTINATION]); - if (session->Type == eSAMSessionTypeDatagram) - d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - else // raw - d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - } - else - LogPrint (eLogError, "SAM: Missing datagram destination"); - } - else - LogPrint (eLogError, "SAM: Session is not created from DATAGRAM SEND"); - } - else - { - LogPrint (eLogWarning, "SAM: Sent datagram size ", size, " exceeds buffer ", len - offset); - return 0; // try to receive more - } - return offset + size; - } + size_t SAMSocket::ProcessDatagramSend(char *buf, size_t len, const char *data) { + LogPrint(eLogDebug, "SAM: Datagram send: ", buf, " ", len); + std::map params; + ExtractParams(buf, params); + size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf; + if (offset + size <= len) { + auto session = m_Owner.FindSession(m_ID); + if (session) { + auto d = session->GetLocalDestination()->GetDatagramDestination(); + if (d) { + i2p::data::IdentityEx dest; + dest.FromBase64(params[SAM_PARAM_DESTINATION]); + if (session->Type == eSAMSessionTypeDatagram) + d->SendDatagramTo((const uint8_t *) data, size, dest.GetIdentHash()); + else // raw + d->SendRawDatagramTo((const uint8_t *) data, size, dest.GetIdentHash()); + } else + LogPrint(eLogError, "SAM: Missing datagram destination"); + } else + LogPrint(eLogError, "SAM: Session is not created from DATAGRAM SEND"); + } else { + LogPrint(eLogWarning, "SAM: Sent datagram size ", size, " exceeds buffer ", len - offset); + return 0; // try to receive more + } + return offset + size; + } - void SAMSocket::ProcessDestGenerate (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Dest generate"); - std::map params; - ExtractParams (buf, params); - // extract signature type - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - auto it = params.find (SAM_PARAM_SIGNATURE_TYPE); - if (it != params.end ()) - { - if (!m_Owner.ResolveSignatureType (it->second, signatureType)) - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); - } - it = params.find (SAM_PARAM_CRYPTO_TYPE); - if (it != params.end ()) - { - try - { - cryptoType = std::stoi(it->second); - } - catch (const std::exception& ex) - { - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); - } - } - auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType); + void SAMSocket::ProcessDestGenerate(char *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Dest generate"); + std::map params; + ExtractParams(buf, params); + // extract signature type + i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + auto it = params.find(SAM_PARAM_SIGNATURE_TYPE); + if (it != params.end()) { + if (!m_Owner.ResolveSignatureType(it->second, signatureType)) + LogPrint(eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); + } + it = params.find(SAM_PARAM_CRYPTO_TYPE); + if (it != params.end()) { + try { + cryptoType = std::stoi(it->second); + } + catch (const std::exception &ex) { + LogPrint(eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what()); + } + } + auto keys = i2p::data::PrivateKeys::CreateRandomKeys(signatureType, cryptoType); #ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, + keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); + size_t l = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, + keys.GetPublic()->ToBase64().c_str(), keys.ToBase64().c_str()); #endif - SendMessageReply (m_Buffer, l, false); - } + SendMessageReply(m_Buffer, l, false); + } - void SAMSocket::ProcessNamingLookup (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Naming lookup: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& name = params[SAM_PARAM_NAME]; - std::shared_ptr identity; - std::shared_ptr addr; - auto session = m_Owner.FindSession(m_ID); - auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->GetLocalDestination (); - if (name == "ME") - SendNamingLookupReply (name, dest->GetIdentity ()); - else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr) - SendNamingLookupReply (name, identity); - else if ((addr = context.GetAddressBook ().GetAddress (name))) - { - if (addr->IsIdentHash ()) - { - auto leaseSet = dest->FindLeaseSet (addr->identHash); - if (leaseSet) - SendNamingLookupReply (name, leaseSet->GetIdentity ()); - else - dest->RequestDestination (addr->identHash, - std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, name)); - } - else - dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, - std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, name)); - } - else - { - LogPrint (eLogError, "SAM: Naming failed, unknown address ", name); + void SAMSocket::ProcessNamingLookup(char *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Naming lookup: ", buf); + std::map params; + ExtractParams(buf, params); + std::string &name = params[SAM_PARAM_NAME]; + std::shared_ptr identity; + std::shared_ptr addr; + auto session = m_Owner.FindSession(m_ID); + auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->GetLocalDestination(); + if (name == "ME") + SendNamingLookupReply(name, dest->GetIdentity()); + else if ((identity = context.GetAddressBook().GetFullAddress(name)) != nullptr) + SendNamingLookupReply(name, identity); + else if ((addr = context.GetAddressBook().GetAddress(name))) { + if (addr->IsIdentHash()) { + auto leaseSet = dest->FindLeaseSet(addr->identHash); + if (leaseSet) + SendNamingLookupReply(name, leaseSet->GetIdentity()); + else + dest->RequestDestination(addr->identHash, + std::bind(&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, + shared_from_this(), std::placeholders::_1, name)); + } else + dest->RequestDestinationWithEncryptedLeaseSet(addr->blindedPublicKey, + std::bind( + &SAMSocket::HandleNamingLookupLeaseSetRequestComplete, + shared_from_this(), std::placeholders::_1, + name)); + } else { + LogPrint(eLogError, "SAM: Naming failed, unknown address ", name); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); - } - } + SendMessageReply(m_Buffer, len, false); + } + } - void SAMSocket::ProcessSessionAdd (char * buf, size_t len) - { - auto session = m_Owner.FindSession(m_ID); - if (session && session->Type == eSAMSessionTypeMaster) - { - LogPrint (eLogDebug, "SAM: Subsession add: ", buf); - auto masterSession = std::static_pointer_cast(session); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - if (masterSession->subsessions.count (id) > 1) - { - // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); - return; - } - std::string& style = params[SAM_PARAM_STYLE]; - SAMSessionType type = eSAMSessionTypeUnknown; - if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; - // TODO: implement other styles - if (type == eSAMSessionTypeUnknown) - { - // unknown style - SendI2PError("Unsupported STYLE"); - return; - } - auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]); - if (fromPort == -1) - { - SendI2PError("Invalid from port"); - return; - } - auto subsession = std::make_shared(masterSession, id, type, fromPort); - if (m_Owner.AddSession (subsession)) - { - masterSession->subsessions.insert (id); - SendSessionCreateReplyOk (); - } - else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); - } - else - SendI2PError ("Wrong session type"); - } + void SAMSocket::ProcessSessionAdd(char *buf, size_t len) { + auto session = m_Owner.FindSession(m_ID); + if (session && session->Type == eSAMSessionTypeMaster) { + LogPrint(eLogDebug, "SAM: Subsession add: ", buf); + auto masterSession = std::static_pointer_cast(session); + std::map params; + ExtractParams(buf, params); + std::string &id = params[SAM_PARAM_ID]; + if (masterSession->subsessions.count(id) > 1) { + // session exists + SendMessageReply(SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); + return; + } + std::string &style = params[SAM_PARAM_STYLE]; + SAMSessionType type = eSAMSessionTypeUnknown; + if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; + // TODO: implement other styles + if (type == eSAMSessionTypeUnknown) { + // unknown style + SendI2PError("Unsupported STYLE"); + return; + } + auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]); + if (fromPort == -1) { + SendI2PError("Invalid from port"); + return; + } + auto subsession = std::make_shared(masterSession, id, type, fromPort); + if (m_Owner.AddSession(subsession)) { + masterSession->subsessions.insert(id); + SendSessionCreateReplyOk(); + } else + SendMessageReply(SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); + } else + SendI2PError("Wrong session type"); + } - void SAMSocket::ProcessSessionRemove (char * buf, size_t len) - { - auto session = m_Owner.FindSession(m_ID); - if (session && session->Type == eSAMSessionTypeMaster) - { - LogPrint (eLogDebug, "SAM: Subsession remove: ", buf); - auto masterSession = std::static_pointer_cast(session); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - if (!masterSession->subsessions.erase (id)) - { - SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false); - return; - } - m_Owner.CloseSession (id); - SendSessionCreateReplyOk (); - } - else - SendI2PError ("Wrong session type"); - } + void SAMSocket::ProcessSessionRemove(char *buf, size_t len) { + auto session = m_Owner.FindSession(m_ID); + if (session && session->Type == eSAMSessionTypeMaster) { + LogPrint(eLogDebug, "SAM: Subsession remove: ", buf); + auto masterSession = std::static_pointer_cast(session); + std::map params; + ExtractParams(buf, params); + std::string &id = params[SAM_PARAM_ID]; + if (!masterSession->subsessions.erase(id)) { + SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false); + return; + } + m_Owner.CloseSession(id); + SendSessionCreateReplyOk(); + } else + SendI2PError("Wrong session type"); + } - void SAMSocket::SendI2PError(const std::string & msg) - { - LogPrint (eLogError, "SAM: I2P error: ", msg); + void SAMSocket::SendI2PError(const std::string &msg) { + LogPrint(eLogError, "SAM: I2P error: ", msg); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); + size_t len = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); #endif - SendMessageReply (m_Buffer, len, true); - } + SendMessageReply(m_Buffer, len, true); + } - void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name) - { - if (leaseSet) - { - context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ()); - SendNamingLookupReply (name, leaseSet->GetIdentity ()); - } - else - { - LogPrint (eLogError, "SAM: Naming lookup failed. LeaseSet for ", name, " not found"); + void SAMSocket::HandleNamingLookupLeaseSetRequestComplete(std::shared_ptr leaseSet, + std::string name) { + if (leaseSet) { + context.GetAddressBook().InsertFullAddress(leaseSet->GetIdentity()); + SendNamingLookupReply(name, leaseSet->GetIdentity()); + } else { + LogPrint(eLogError, "SAM: Naming lookup failed. LeaseSet for ", name, " not found"); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); - } - } + SendMessageReply(m_Buffer, len, false); + } + } - void SAMSocket::SendNamingLookupReply (const std::string& name, std::shared_ptr identity) - { - auto base64 = identity->ToBase64 (); + void SAMSocket::SendNamingLookupReply(const std::string &name, + std::shared_ptr identity) { + auto base64 = identity->ToBase64(); #ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ()); + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ()); #else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ()); + size_t l = snprintf(m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str(), base64.c_str()); #endif - SendMessageReply (m_Buffer, l, false); - } + SendMessageReply(m_Buffer, l, false); + } - void SAMSocket::ExtractParams (char * buf, std::map& params) - { - char * separator; - do - { - separator = strchr (buf, ' '); - if (separator) *separator = 0; - char * value = strchr (buf, '='); - if (value) - { - *value = 0; - value++; - params[buf] = value; - } - buf = separator + 1; - } - while (separator); - } + void SAMSocket::ExtractParams(char *buf, std::map ¶ms) { + char *separator; + do { + separator = strchr(buf, ' '); + if (separator) *separator = 0; + char *value = strchr(buf, '='); + if (value) { + *value = 0; + value++; + params[buf] = value; + } + buf = separator + 1; + } while (separator); + } - void SAMSocket::Receive () - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), - std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + void SAMSocket::Receive() { + m_Socket.async_read_some( + boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), + std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived + : &SAMSocket::HandleMessage, + shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + } - void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("read error"); - } - else - { - if (m_Stream) - { - bytes_transferred += m_BufferOffset; - m_BufferOffset = 0; - m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, - std::bind(&SAMSocket::HandleStreamSend, shared_from_this(), std::placeholders::_1)); - } - else - { - Terminate("No Stream Remaining"); - } - } - } + void SAMSocket::HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "SAM: Read error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate("read error"); + } else { + if (m_Stream) { + bytes_transferred += m_BufferOffset; + m_BufferOffset = 0; + m_Stream->AsyncSend((uint8_t *) m_Buffer, bytes_transferred, + std::bind(&SAMSocket::HandleStreamSend, shared_from_this(), + std::placeholders::_1)); + } else { + Terminate("No Stream Remaining"); + } + } + } - void SAMSocket::I2PReceive () - { - if (m_Stream) - { - if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || - m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular - { - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), - std::bind (&SAMSocket::HandleI2PReceive, shared_from_this(), - std::placeholders::_1, std::placeholders::_2), - SAM_SOCKET_CONNECTION_MAX_IDLE); - } - else // closed by peer - { - uint8_t * buff = new uint8_t[SAM_SOCKET_BUFFER_SIZE]; - // get remaining data - auto len = m_Stream->ReadSome (buff, SAM_SOCKET_BUFFER_SIZE); - if (len > 0) // still some data - { - WriteI2PDataImmediate(buff, len); - } - else // no more data - { - delete [] buff; - Terminate ("no more data"); - } - } - } - } + void SAMSocket::I2PReceive() { + if (m_Stream) { + if (m_Stream->GetStatus() == i2p::stream::eStreamStatusNew || + m_Stream->GetStatus() == i2p::stream::eStreamStatusOpen) // regular + { + m_Stream->AsyncReceive(boost::asio::buffer(m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleI2PReceive, shared_from_this(), + std::placeholders::_1, std::placeholders::_2), + SAM_SOCKET_CONNECTION_MAX_IDLE); + } else // closed by peer + { + uint8_t *buff = new uint8_t[SAM_SOCKET_BUFFER_SIZE]; + // get remaining data + auto len = m_Stream->ReadSome(buff, SAM_SOCKET_BUFFER_SIZE); + if (len > 0) // still some data + { + WriteI2PDataImmediate(buff, len); + } else // no more data + { + delete[] buff; + Terminate("no more data"); + } + } + } + } - void SAMSocket::WriteI2PDataImmediate(uint8_t * buff, size_t sz) - { - boost::asio::async_write ( - m_Socket, - boost::asio::buffer (buff, sz), - boost::asio::transfer_all(), - std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination - } + void SAMSocket::WriteI2PDataImmediate(uint8_t *buff, size_t sz) { + boost::asio::async_write( + m_Socket, + boost::asio::buffer(buff, sz), + boost::asio::transfer_all(), + std::bind(&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this(), std::placeholders::_1, + buff)); // postpone termination + } - void SAMSocket::HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff) - { - delete [] buff; - } + void SAMSocket::HandleWriteI2PDataImmediate(const boost::system::error_code &ec, uint8_t *buff) { + delete[] buff; + } - void SAMSocket::WriteI2PData(size_t sz) - { - boost::asio::async_write ( - m_Socket, - boost::asio::buffer (m_StreamBuffer, sz), - boost::asio::transfer_all(), - std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } + void SAMSocket::WriteI2PData(size_t sz) { + boost::asio::async_write( + m_Socket, + boost::asio::buffer(m_StreamBuffer, sz), + boost::asio::transfer_all(), + std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, + std::placeholders::_2)); + } - void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Stream read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - { - if (bytes_transferred > 0) - { - WriteI2PData(bytes_transferred); - } - else - { - auto s = shared_from_this (); - m_Owner.GetService ().post ([s] { s->Terminate ("stream read error"); }); - } - } - else - { - auto s = shared_from_this (); - m_Owner.GetService ().post ([s] { s->Terminate ("stream read error (op aborted)"); }); - } - } - else - { - if (m_SocketType != eSAMSocketTypeTerminated) - { - if (bytes_transferred > 0) - { - WriteI2PData(bytes_transferred); - } - else - I2PReceive(); - } - } - } + void SAMSocket::HandleI2PReceive(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "SAM: Stream read error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) { + if (bytes_transferred > 0) { + WriteI2PData(bytes_transferred); + } else { + auto s = shared_from_this(); + m_Owner.GetService().post([s] { s->Terminate("stream read error"); }); + } + } else { + auto s = shared_from_this(); + m_Owner.GetService().post([s] { s->Terminate("stream read error (op aborted)"); }); + } + } else { + if (m_SocketType != eSAMSocketTypeTerminated) { + if (bytes_transferred > 0) { + WriteI2PData(bytes_transferred); + } else + I2PReceive(); + } + } + } - void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode, size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Socket write error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("socket write error at HandleWriteI2PData"); - } - else - { - I2PReceive (); - } - } + void SAMSocket::HandleWriteI2PData(const boost::system::error_code &ecode, size_t bytes_transferred) { + if (ecode) { + LogPrint(eLogError, "SAM: Socket write error: ", ecode.message()); + if (ecode != boost::asio::error::operation_aborted) + Terminate("socket write error at HandleWriteI2PData"); + } else { + I2PReceive(); + } + } - void SAMSocket::HandleI2PAccept (std::shared_ptr stream) - { - if (stream) - { - LogPrint (eLogDebug, "SAM: Incoming I2P connection for session ", m_ID); - m_SocketType = eSAMSocketTypeStream; - m_IsAccepting = false; - m_Stream = stream; - context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ()); - auto session = m_Owner.FindSession (m_ID); - if (session) - { - // find more pending acceptors - for (auto & it: m_Owner.ListSockets (m_ID)) - if (it->m_SocketType == eSAMSocketTypeAcceptor) - { - it->m_IsAccepting = true; - session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1)); - break; - } - } - if (!m_IsSilent) - { - // get remote peer address - auto ident_ptr = stream->GetRemoteIdentity(); - const size_t ident_len = ident_ptr->GetFullLen(); - uint8_t* ident = new uint8_t[ident_len]; + void SAMSocket::HandleI2PAccept(std::shared_ptr stream) { + if (stream) { + LogPrint(eLogDebug, "SAM: Incoming I2P connection for session ", m_ID); + m_SocketType = eSAMSocketTypeStream; + m_IsAccepting = false; + m_Stream = stream; + context.GetAddressBook().InsertFullAddress(stream->GetRemoteIdentity()); + auto session = m_Owner.FindSession(m_ID); + if (session) { + // find more pending acceptors + for (auto &it: m_Owner.ListSockets(m_ID)) + if (it->m_SocketType == eSAMSocketTypeAcceptor) { + it->m_IsAccepting = true; + session->GetLocalDestination()->AcceptOnce( + std::bind(&SAMSocket::HandleI2PAccept, it, std::placeholders::_1)); + break; + } + } + if (!m_IsSilent) { + // get remote peer address + auto ident_ptr = stream->GetRemoteIdentity(); + const size_t ident_len = ident_ptr->GetFullLen(); + uint8_t *ident = new uint8_t[ident_len]; - // send remote peer address as base64 - const size_t l = ident_ptr->ToBuffer (ident, ident_len); - const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE); - delete[] ident; - m_StreamBuffer[l1] = '\n'; - HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream - } - else - I2PReceive (); - } - else - LogPrint (eLogWarning, "SAM: I2P acceptor has been reset"); - } + // send remote peer address as base64 + const size_t l = ident_ptr->ToBuffer(ident, ident_len); + const size_t l1 = i2p::data::ByteStreamToBase64(ident, l, (char *) m_StreamBuffer, + SAM_SOCKET_BUFFER_SIZE); + delete[] ident; + m_StreamBuffer[l1] = '\n'; + HandleI2PReceive(boost::system::error_code(), + l1 + 1); // we send identity like it has been received from stream + } else + I2PReceive(); + } else + LogPrint(eLogWarning, "SAM: I2P acceptor has been reset"); + } - void SAMSocket::HandleI2PForward (std::shared_ptr stream, - boost::asio::ip::tcp::endpoint ep) - { - if (stream) - { - LogPrint (eLogDebug, "SAM: Incoming forward I2P connection for session ", m_ID); - auto newSocket = std::make_shared(m_Owner); - newSocket->SetSocketType (eSAMSocketTypeStream); - auto s = shared_from_this (); - newSocket->GetSocket ().async_connect (ep, - [s, newSocket, stream](const boost::system::error_code& ecode) - { - if (!ecode) - { - s->m_Owner.AddSocket (newSocket); - newSocket->Receive (); - newSocket->m_Stream = stream; - newSocket->m_ID = s->m_ID; - if (!s->m_IsSilent) - { - // get remote peer address - auto dest = stream->GetRemoteIdentity()->ToBase64 (); - memcpy (newSocket->m_StreamBuffer, dest.c_str (), dest.length ()); - newSocket->m_StreamBuffer[dest.length ()] = '\n'; - newSocket->HandleI2PReceive (boost::system::error_code (),dest.length () + 1); // we send identity like it has been received from stream - } - else - newSocket->I2PReceive (); - } - else - stream->AsyncClose (); - }); - } - else - LogPrint (eLogWarning, "SAM: I2P forward acceptor has been reset"); - } + void SAMSocket::HandleI2PForward(std::shared_ptr stream, + boost::asio::ip::tcp::endpoint ep) { + if (stream) { + LogPrint(eLogDebug, "SAM: Incoming forward I2P connection for session ", m_ID); + auto newSocket = std::make_shared(m_Owner); + newSocket->SetSocketType(eSAMSocketTypeStream); + auto s = shared_from_this(); + newSocket->GetSocket().async_connect(ep, + [s, newSocket, stream](const boost::system::error_code &ecode) { + if (!ecode) { + s->m_Owner.AddSocket(newSocket); + newSocket->Receive(); + newSocket->m_Stream = stream; + newSocket->m_ID = s->m_ID; + if (!s->m_IsSilent) { + // get remote peer address + auto dest = stream->GetRemoteIdentity()->ToBase64(); + memcpy(newSocket->m_StreamBuffer, dest.c_str(), + dest.length()); + newSocket->m_StreamBuffer[dest.length()] = '\n'; + newSocket->HandleI2PReceive( + boost::system::error_code(), dest.length() + + 1); // we send identity like it has been received from stream + } else + newSocket->I2PReceive(); + } else + stream->AsyncClose(); + }); + } else + LogPrint(eLogWarning, "SAM: I2P forward acceptor has been reset"); + } - void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Datagram received ", len); - auto base64 = from.ToBase64 (); - auto session = m_Owner.FindSession(m_ID); - if(session) - { - auto ep = session->UDPEndpoint; - if (ep) - { - // udp forward enabled - const char lf = '\n'; - // send to remote endpoint, { destination, linefeed, payload } - m_Owner.SendTo({ {(const uint8_t *)base64.c_str(), base64.size()}, {(const uint8_t *)&lf, 1}, {buf, len} }, *ep); - } - else - { + void SAMSocket::HandleI2PDatagramReceive(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Datagram received ", len); + auto base64 = from.ToBase64(); + auto session = m_Owner.FindSession(m_ID); + if (session) { + auto ep = session->UDPEndpoint; + if (ep) { + // udp forward enabled + const char lf = '\n'; + // send to remote endpoint, { destination, linefeed, payload } + m_Owner.SendTo({{(const uint8_t *) base64.c_str(), base64.size()}, + {(const uint8_t *) &lf, 1}, + {buf, len}}, *ep); + } else { #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = snprintf((char *) m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, + base64.c_str(), (long unsigned int) len); #endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - WriteI2PData(len + l); - } - else - LogPrint (eLogWarning, "SAM: Received datagram size ", len," exceeds buffer"); - } - } - } + if (len < SAM_SOCKET_BUFFER_SIZE - l) { + memcpy(m_StreamBuffer + l, buf, len); + WriteI2PData(len + l); + } else + LogPrint(eLogWarning, "SAM: Received datagram size ", len, " exceeds buffer"); + } + } + } - void SAMSocket::HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Raw datagram received ", len); - auto session = m_Owner.FindSession(m_ID); - if(session) - { - auto ep = session->UDPEndpoint; - if (ep) - // udp forward enabled - m_Owner.SendTo({ {buf, len} }, *ep); - else - { + void + SAMSocket::HandleI2PRawDatagramReceive(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len) { + LogPrint(eLogDebug, "SAM: Raw datagram received ", len); + auto session = m_Owner.FindSession(m_ID); + if (session) { + auto ep = session->UDPEndpoint; + if (ep) + // udp forward enabled + m_Owner.SendTo({{buf, len}}, *ep); + else { #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len); #else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len); + size_t l = snprintf((char *) m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, + (long unsigned int) len); #endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - WriteI2PData(len + l); - } - else - LogPrint (eLogWarning, "SAM: Received raw datagram size ", len," exceeds buffer"); - } - } - } + if (len < SAM_SOCKET_BUFFER_SIZE - l) { + memcpy(m_StreamBuffer + l, buf, len); + WriteI2PData(len + l); + } else + LogPrint(eLogWarning, "SAM: Received raw datagram size ", len, " exceeds buffer"); + } + } + } - void SAMSocket::HandleStreamSend(const boost::system::error_code & ec) - { - m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); - } + void SAMSocket::HandleStreamSend(const boost::system::error_code &ec) { + m_Owner.GetService().post( + std::bind(!ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); + } - SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type): - m_Bridge(parent), Name(id), Type (type), UDPEndpoint(nullptr) - { - } + SAMSession::SAMSession(SAMBridge &parent, const std::string &id, SAMSessionType type) : + m_Bridge(parent), Name(id), Type(type), UDPEndpoint(nullptr) { + } - void SAMSession::CloseStreams () - { - for(const auto & itr : m_Bridge.ListSockets(Name)) - { - itr->Terminate(nullptr); - } - } + void SAMSession::CloseStreams() { + for (const auto &itr: m_Bridge.ListSockets(Name)) { + itr->Terminate(nullptr); + } + } - SAMSingleSession::SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr dest): - SAMSession (parent, name, type), - localDestination (dest) - { - } + SAMSingleSession::SAMSingleSession(SAMBridge &parent, const std::string &name, SAMSessionType type, + std::shared_ptr dest) : + SAMSession(parent, name, type), + localDestination(dest) { + } - SAMSingleSession::~SAMSingleSession () - { - i2p::client::context.DeleteLocalDestination (localDestination); - } + SAMSingleSession::~SAMSingleSession() { + i2p::client::context.DeleteLocalDestination(localDestination); + } - void SAMSingleSession::StopLocalDestination () - { - localDestination->Release (); - // stop accepting new streams - localDestination->StopAcceptingStreams (); - // terminate existing streams - auto s = localDestination->GetStreamingDestination (); // TODO: take care about datagrams - if (s) s->Stop (); - } + void SAMSingleSession::StopLocalDestination() { + localDestination->Release(); + // stop accepting new streams + localDestination->StopAcceptingStreams(); + // terminate existing streams + auto s = localDestination->GetStreamingDestination(); // TODO: take care about datagrams + if (s) s->Stop(); + } - void SAMMasterSession::Close () - { - SAMSingleSession::Close (); - for (const auto& it: subsessions) - m_Bridge.CloseSession (it); - subsessions.clear (); - } + void SAMMasterSession::Close() { + SAMSingleSession::Close(); + for (const auto &it: subsessions) + m_Bridge.CloseSession(it); + subsessions.clear(); + } - SAMSubSession::SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, int port): - SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port) - { - if (Type == eSAMSessionTypeStream) - { - auto d = masterSession->GetLocalDestination ()->CreateStreamingDestination (inPort); - if (d) d->Start (); - } - // TODO: implement datagrams - } + SAMSubSession::SAMSubSession(std::shared_ptr master, const std::string &name, + SAMSessionType type, int port) : + SAMSession(master->m_Bridge, name, type), masterSession(master), inPort(port) { + if (Type == eSAMSessionTypeStream) { + auto d = masterSession->GetLocalDestination()->CreateStreamingDestination(inPort); + if (d) d->Start(); + } + // TODO: implement datagrams + } - std::shared_ptr SAMSubSession::GetLocalDestination () - { - return masterSession ? masterSession->GetLocalDestination () : nullptr; - } + std::shared_ptr SAMSubSession::GetLocalDestination() { + return masterSession ? masterSession->GetLocalDestination() : nullptr; + } - void SAMSubSession::StopLocalDestination () - { - auto dest = GetLocalDestination (); - if (dest && Type == eSAMSessionTypeStream) - { - auto d = dest->RemoveStreamingDestination (inPort); - if (d) d->Stop (); - } - // TODO: implement datagrams - } + void SAMSubSession::StopLocalDestination() { + auto dest = GetLocalDestination(); + if (dest && Type == eSAMSessionTypeStream) { + auto d = dest->RemoveStreamingDestination(inPort); + if (d) d->Stop(); + } + // TODO: implement datagrams + } - SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread): - RunnableService ("SAM"), m_IsSingleThread (singleThread), - m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), - m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), - m_SignatureTypes - { - {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, - {"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256}, - {"ECDSA_SHA384_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384}, - {"ECDSA_SHA512_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521}, - {"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519}, - {"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256}, - {"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512}, - {"RedDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519}, - } - { - } + SAMBridge::SAMBridge(const std::string &address, int port, bool singleThread) : + RunnableService("SAM"), m_IsSingleThread(singleThread), + m_Acceptor(GetIOService(), + boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), + m_DatagramEndpoint(boost::asio::ip::address::from_string(address), port - 1), + m_DatagramSocket(GetIOService(), m_DatagramEndpoint), + m_SignatureTypes + { + {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, + {"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256}, + {"ECDSA_SHA384_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384}, + {"ECDSA_SHA512_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521}, + {"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519}, + {"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256}, + {"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512}, + {"RedDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519}, + } { + } - SAMBridge::~SAMBridge () - { - if (IsRunning ()) - Stop (); - } + SAMBridge::~SAMBridge() { + if (IsRunning()) + Stop(); + } - void SAMBridge::Start () - { - Accept (); - ReceiveDatagram (); - StartIOService (); - } + void SAMBridge::Start() { + Accept(); + ReceiveDatagram(); + StartIOService(); + } - void SAMBridge::Stop () - { - try - { - m_Acceptor.cancel (); - } - catch (const std::exception& ex) - { - LogPrint (eLogError, "SAM: Runtime exception: ", ex.what ()); - } + void SAMBridge::Stop() { + try { + m_Acceptor.cancel(); + } + catch (const std::exception &ex) { + LogPrint(eLogError, "SAM: Runtime exception: ", ex.what()); + } - { - std::unique_lock l(m_SessionsMutex); - for (auto& it: m_Sessions) - it.second->Close (); - m_Sessions.clear (); - } - StopIOService (); - } + { + std::unique_lock l(m_SessionsMutex); + for (auto &it: m_Sessions) + it.second->Close(); + m_Sessions.clear(); + } + StopIOService(); + } - void SAMBridge::Accept () - { - auto newSocket = std::make_shared(*this); - m_Acceptor.async_accept (newSocket->GetSocket(), std::bind (&SAMBridge::HandleAccept, this, - std::placeholders::_1, newSocket)); - } + void SAMBridge::Accept() { + auto newSocket = std::make_shared(*this); + m_Acceptor.async_accept(newSocket->GetSocket(), std::bind(&SAMBridge::HandleAccept, this, + std::placeholders::_1, newSocket)); + } - void SAMBridge::AddSocket(std::shared_ptr socket) - { - std::unique_lock lock(m_OpenSocketsMutex); - m_OpenSockets.push_back(socket); - } + void SAMBridge::AddSocket(std::shared_ptr socket) { + std::unique_lock lock(m_OpenSocketsMutex); + m_OpenSockets.push_back(socket); + } - void SAMBridge::RemoveSocket(const std::shared_ptr & socket) - { - std::unique_lock lock(m_OpenSocketsMutex); - m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item == socket; }); - } + void SAMBridge::RemoveSocket(const std::shared_ptr &socket) { + std::unique_lock lock(m_OpenSocketsMutex); + m_OpenSockets.remove_if( + [socket](const std::shared_ptr &item) -> bool { return item == socket; }); + } - void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (!ecode) - { - boost::system::error_code ec; - auto ep = socket->GetSocket ().remote_endpoint (ec); - if (!ec) - { - LogPrint (eLogDebug, "SAM: New connection from ", ep); - AddSocket (socket); - socket->ReceiveHandshake (); - } - else - LogPrint (eLogError, "SAM: Incoming connection error: ", ec.message ()); - } - else - LogPrint (eLogError, "SAM: Accept error: ", ecode.message ()); + void SAMBridge::HandleAccept(const boost::system::error_code &ecode, std::shared_ptr socket) { + if (!ecode) { + boost::system::error_code ec; + auto ep = socket->GetSocket().remote_endpoint(ec); + if (!ec) { + LogPrint(eLogDebug, "SAM: New connection from ", ep); + AddSocket(socket); + socket->ReceiveHandshake(); + } else + LogPrint(eLogError, "SAM: Incoming connection error: ", ec.message()); + } else + LogPrint(eLogError, "SAM: Accept error: ", ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Accept (); - } + if (ecode != boost::asio::error::operation_aborted) + Accept(); + } - std::shared_ptr SAMBridge::CreateSession (const std::string& id, SAMSessionType type, - const std::string& destination, const std::map * params) - { - std::shared_ptr localDestination = nullptr; - if (destination != "") - { - i2p::data::PrivateKeys keys; - if (!keys.FromBase64 (destination)) return nullptr; - localDestination = m_IsSingleThread ? - i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) : - i2p::client::context.CreateNewLocalDestination (keys, true, params); - } - else // transient - { - // extract signature type - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - if (params) - { - auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); - if (it != params->end ()) - { - if (!ResolveSignatureType (it->second, signatureType)) - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); - } - it = params->find (SAM_PARAM_CRYPTO_TYPE); - if (it != params->end ()) - { - try - { - cryptoType = std::stoi(it->second); - } - catch (const std::exception& ex) - { - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); - } - } - } - localDestination = m_IsSingleThread ? - i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) : - i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); - } - if (localDestination) - { - localDestination->Acquire (); - auto session = (type == eSAMSessionTypeMaster) ? std::make_shared(*this, id, localDestination) : - std::make_shared(*this, id, type, localDestination); - std::unique_lock l(m_SessionsMutex); - auto ret = m_Sessions.insert (std::make_pair(id, session)); - if (!ret.second) - LogPrint (eLogWarning, "SAM: Session ", id, " already exists"); - return ret.first->second; - } - return nullptr; - } + std::shared_ptr SAMBridge::CreateSession(const std::string &id, SAMSessionType type, + const std::string &destination, + const std::map *params) { + std::shared_ptr localDestination = nullptr; + if (destination != "") { + i2p::data::PrivateKeys keys; + if (!keys.FromBase64(destination)) return nullptr; + localDestination = m_IsSingleThread ? + i2p::client::context.CreateNewLocalDestination(GetIOService(), keys, true, params) : + i2p::client::context.CreateNewLocalDestination(keys, true, params); + } else // transient + { + // extract signature type + i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + if (params) { + auto it = params->find(SAM_PARAM_SIGNATURE_TYPE); + if (it != params->end()) { + if (!ResolveSignatureType(it->second, signatureType)) + LogPrint(eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); + } + it = params->find(SAM_PARAM_CRYPTO_TYPE); + if (it != params->end()) { + try { + cryptoType = std::stoi(it->second); + } + catch (const std::exception &ex) { + LogPrint(eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what()); + } + } + } + localDestination = m_IsSingleThread ? + i2p::client::context.CreateNewLocalDestination(GetIOService(), true, signatureType, + cryptoType, params) : + i2p::client::context.CreateNewLocalDestination(true, signatureType, cryptoType, + params); + } + if (localDestination) { + localDestination->Acquire(); + auto session = (type == eSAMSessionTypeMaster) ? std::make_shared(*this, id, + localDestination) : + std::make_shared(*this, id, type, localDestination); + std::unique_lock l(m_SessionsMutex); + auto ret = m_Sessions.insert(std::make_pair(id, session)); + if (!ret.second) + LogPrint(eLogWarning, "SAM: Session ", id, " already exists"); + return ret.first->second; + } + return nullptr; + } - bool SAMBridge::AddSession (std::shared_ptr session) - { - if (!session) return false; - auto ret = m_Sessions.emplace (session->Name, session); - return ret.second; - } + bool SAMBridge::AddSession(std::shared_ptr session) { + if (!session) return false; + auto ret = m_Sessions.emplace(session->Name, session); + return ret.second; + } - void SAMBridge::CloseSession (const std::string& id) - { - std::shared_ptr session; - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (id); - if (it != m_Sessions.end ()) - { - session = it->second; - m_Sessions.erase (it); - } - } - if (session) - { - session->StopLocalDestination (); - session->Close (); - if (m_IsSingleThread) - { - auto timer = std::make_shared(GetService ()); - timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds - timer->async_wait ([timer, session](const boost::system::error_code& ecode) - { - // session's destructor is called here - }); - } - } - } + void SAMBridge::CloseSession(const std::string &id) { + std::shared_ptr session; + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find(id); + if (it != m_Sessions.end()) { + session = it->second; + m_Sessions.erase(it); + } + } + if (session) { + session->StopLocalDestination(); + session->Close(); + if (m_IsSingleThread) { + auto timer = std::make_shared(GetService()); + timer->expires_from_now(boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds + timer->async_wait([timer, session](const boost::system::error_code &ecode) { + // session's destructor is called here + }); + } + } + } - std::shared_ptr SAMBridge::FindSession (const std::string& id) const - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (id); - if (it != m_Sessions.end ()) - return it->second; - return nullptr; - } + std::shared_ptr SAMBridge::FindSession(const std::string &id) const { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find(id); + if (it != m_Sessions.end()) + return it->second; + return nullptr; + } - std::list > SAMBridge::ListSockets(const std::string & id) const - { - std::list > list; - { - std::unique_lock l(m_OpenSocketsMutex); - for (const auto & itr : m_OpenSockets) - if (itr->IsSession(id)) - list.push_back(itr); - } - return list; - } + std::list > SAMBridge::ListSockets(const std::string &id) const { + std::list > list; + { + std::unique_lock l(m_OpenSocketsMutex); + for (const auto &itr: m_OpenSockets) + if (itr->IsSession(id)) + list.push_back(itr); + } + return list; + } - void SAMBridge::SendTo (const std::vector& bufs, const boost::asio::ip::udp::endpoint& ep) - { - m_DatagramSocket.send_to (bufs, ep); - } + void SAMBridge::SendTo(const std::vector &bufs, + const boost::asio::ip::udp::endpoint &ep) { + m_DatagramSocket.send_to(bufs, ep); + } - void SAMBridge::ReceiveDatagram () - { - m_DatagramSocket.async_receive_from ( - boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), - m_SenderEndpoint, - std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); - } + void SAMBridge::ReceiveDatagram() { + m_DatagramSocket.async_receive_from( + boost::asio::buffer(m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), + m_SenderEndpoint, + std::bind(&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); + } - void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode) - { - m_DatagramReceiveBuffer[bytes_transferred] = 0; - char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); - if(eol) - { - *eol = 0; eol++; - size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); - LogPrint (eLogDebug, "SAM: Datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); - char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); - if (sessionID) - { - sessionID++; - char * destination = strchr (sessionID, ' '); - if (destination) - { - *destination = 0; destination++; - auto session = FindSession (sessionID); - if (session) - { - auto localDest = session->GetLocalDestination (); - auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr; - if (datagramDest) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (destination); - if (session->Type == eSAMSessionTypeDatagram) - datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - else if (session->Type == eSAMSessionTypeRaw) - datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - else - LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID); - } - else - LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID); - } - else - LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); - } - else - LogPrint (eLogError, "SAM: Missing destination key"); - } - else - LogPrint (eLogError, "SAM: Missing sessionID"); - } - else - LogPrint(eLogError, "SAM: Invalid datagram"); - ReceiveDatagram (); - } - else - LogPrint (eLogError, "SAM: Datagram receive error: ", ecode.message ()); - } + void SAMBridge::HandleReceivedDatagram(const boost::system::error_code &ecode, std::size_t bytes_transferred) { + if (!ecode) { + m_DatagramReceiveBuffer[bytes_transferred] = 0; + char *eol = strchr((char *) m_DatagramReceiveBuffer, '\n'); + if (eol) { + *eol = 0; + eol++; + size_t payloadLen = bytes_transferred - ((uint8_t *) eol - m_DatagramReceiveBuffer); + LogPrint(eLogDebug, "SAM: Datagram received ", m_DatagramReceiveBuffer, " size=", payloadLen); + char *sessionID = strchr((char *) m_DatagramReceiveBuffer, ' '); + if (sessionID) { + sessionID++; + char *destination = strchr(sessionID, ' '); + if (destination) { + *destination = 0; + destination++; + auto session = FindSession(sessionID); + if (session) { + auto localDest = session->GetLocalDestination(); + auto datagramDest = localDest ? localDest->GetDatagramDestination() : nullptr; + if (datagramDest) { + i2p::data::IdentityEx dest; + dest.FromBase64(destination); + if (session->Type == eSAMSessionTypeDatagram) + datagramDest->SendDatagramTo((uint8_t *) eol, payloadLen, dest.GetIdentHash()); + else if (session->Type == eSAMSessionTypeRaw) + datagramDest->SendRawDatagramTo((uint8_t *) eol, payloadLen, + dest.GetIdentHash()); + else + LogPrint(eLogError, "SAM: Unexpected session type ", (int) session->Type, + "for session ", sessionID); + } else + LogPrint(eLogError, "SAM: Datagram destination is not set for session ", sessionID); + } else + LogPrint(eLogError, "SAM: Session ", sessionID, " not found"); + } else + LogPrint(eLogError, "SAM: Missing destination key"); + } else + LogPrint(eLogError, "SAM: Missing sessionID"); + } else + LogPrint(eLogError, "SAM: Invalid datagram"); + ReceiveDatagram(); + } else + LogPrint(eLogError, "SAM: Datagram receive error: ", ecode.message()); + } - bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const - { - try - { - type = std::stoi (name); - } - catch (const std::invalid_argument& ex) - { - // name is not numeric, resolving - auto it = m_SignatureTypes.find (name); - if (it != m_SignatureTypes.end ()) - type = it->second; - else - return false; - } - catch (const std::exception& ex) - { - return false; - } - // name has been resolved - return true; - } -} + bool SAMBridge::ResolveSignatureType(const std::string &name, i2p::data::SigningKeyType &type) const { + try { + type = std::stoi(name); + } + catch (const std::invalid_argument &ex) { + // name is not numeric, resolving + auto it = m_SignatureTypes.find(name); + if (it != m_SignatureTypes.end()) + type = it->second; + else + return false; + } + catch (const std::exception &ex) { + return false; + } + // name has been resolved + return true; + } + } } diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 88990d7c..9e3eac55 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -24,265 +24,318 @@ #include "Streaming.h" #include "Destination.h" -namespace i2p -{ -namespace client -{ - const size_t SAM_SOCKET_BUFFER_SIZE = 8192; - const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds - const char SAM_HANDSHAKE[] = "HELLO VERSION"; - const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; - const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; - const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; - const char SAM_SESSION_CREATE[] = "SESSION CREATE"; - const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; - const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; - const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; - const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n"; - const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; - const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; - const char SAM_SESSION_ADD[] = "SESSION ADD"; - const char SAM_SESSION_REMOVE[] = "SESSION REMOVE"; - const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; - const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; - const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; - const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n"; - const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; - const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; - const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; - const char SAM_STREAM_FORWARD[] = "STREAM FORWARD"; - const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; - const char SAM_RAW_SEND[] = "RAW SEND"; - const char SAM_DEST_GENERATE[] = "DEST GENERATE"; - const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; - const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; - const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; - const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=%s VALUE=%s\n"; - const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; - const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n"; - const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; - const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=KEY_NOT_FOUND NAME=%s\n"; - const char SAM_PARAM_MIN[] = "MIN"; - const char SAM_PARAM_MAX[] = "MAX"; - const char SAM_PARAM_STYLE[] = "STYLE"; - const char SAM_PARAM_ID[] = "ID"; - const char SAM_PARAM_SILENT[] = "SILENT"; - const char SAM_PARAM_DESTINATION[] = "DESTINATION"; - const char SAM_PARAM_NAME[] = "NAME"; - const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; - const char SAM_PARAM_CRYPTO_TYPE[] = "CRYPTO_TYPE"; - const char SAM_PARAM_SIZE[] = "SIZE"; - const char SAM_PARAM_HOST[] = "HOST"; - const char SAM_PARAM_PORT[] = "PORT"; - const char SAM_PARAM_FROM_PORT[] = "FROM_PORT"; - const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; - const char SAM_VALUE_STREAM[] = "STREAM"; - const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; - const char SAM_VALUE_RAW[] = "RAW"; - const char SAM_VALUE_MASTER[] = "MASTER"; - const char SAM_VALUE_TRUE[] = "true"; - const char SAM_VALUE_FALSE[] = "false"; +namespace i2p { + namespace client { + const size_t SAM_SOCKET_BUFFER_SIZE = 8192; + const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds + const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const char SAM_HANDSHAKE[] = "HELLO VERSION"; + const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; + const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; + const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; + const char SAM_SESSION_CREATE[] = "SESSION CREATE"; + const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; + const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; + const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; + const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n"; + const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; + const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; + const char SAM_SESSION_ADD[] = "SESSION ADD"; + const char SAM_SESSION_REMOVE[] = "SESSION REMOVE"; + const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; + const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; + const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; + const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n"; + const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; + const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; + const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; + const char SAM_STREAM_FORWARD[] = "STREAM FORWARD"; + const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; + const char SAM_RAW_SEND[] = "RAW SEND"; + const char SAM_DEST_GENERATE[] = "DEST GENERATE"; + const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; + const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; + const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; + const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=%s VALUE=%s\n"; + const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; + const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n"; + const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; + const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=KEY_NOT_FOUND NAME=%s\n"; + const char SAM_PARAM_MIN[] = "MIN"; + const char SAM_PARAM_MAX[] = "MAX"; + const char SAM_PARAM_STYLE[] = "STYLE"; + const char SAM_PARAM_ID[] = "ID"; + const char SAM_PARAM_SILENT[] = "SILENT"; + const char SAM_PARAM_DESTINATION[] = "DESTINATION"; + const char SAM_PARAM_NAME[] = "NAME"; + const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; + const char SAM_PARAM_CRYPTO_TYPE[] = "CRYPTO_TYPE"; + const char SAM_PARAM_SIZE[] = "SIZE"; + const char SAM_PARAM_HOST[] = "HOST"; + const char SAM_PARAM_PORT[] = "PORT"; + const char SAM_PARAM_FROM_PORT[] = "FROM_PORT"; + const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; + const char SAM_VALUE_STREAM[] = "STREAM"; + const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; + const char SAM_VALUE_RAW[] = "RAW"; + const char SAM_VALUE_MASTER[] = "MASTER"; + const char SAM_VALUE_TRUE[] = "true"; + const char SAM_VALUE_FALSE[] = "false"; - enum SAMSocketType - { - eSAMSocketTypeUnknown, - eSAMSocketTypeSession, - eSAMSocketTypeStream, - eSAMSocketTypeAcceptor, - eSAMSocketTypeForward, - eSAMSocketTypeTerminated - }; + enum SAMSocketType { + eSAMSocketTypeUnknown, + eSAMSocketTypeSession, + eSAMSocketTypeStream, + eSAMSocketTypeAcceptor, + eSAMSocketTypeForward, + eSAMSocketTypeTerminated + }; - class SAMBridge; - struct SAMSession; - class SAMSocket: public std::enable_shared_from_this - { - public: + class SAMBridge; - typedef boost::asio::ip::tcp::socket Socket_t; - SAMSocket (SAMBridge& owner); - ~SAMSocket (); + struct SAMSession; - Socket_t& GetSocket () { return m_Socket; }; - void ReceiveHandshake (); - void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; - SAMSocketType GetSocketType () const { return m_SocketType; }; + class SAMSocket : public std::enable_shared_from_this { + public: - void Terminate (const char* reason); + typedef boost::asio::ip::tcp::socket Socket_t; - bool IsSession(const std::string & id) const; + SAMSocket(SAMBridge &owner); - private: + ~SAMSocket(); - void TerminateClose() { Terminate(nullptr); } + Socket_t &GetSocket() { return m_Socket; }; - void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendMessageReply (const char * msg, size_t len, bool close); - void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void ReceiveHandshake(); - void I2PReceive (); - void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleI2PAccept (std::shared_ptr stream); - void HandleI2PForward (std::shared_ptr stream, boost::asio::ip::tcp::endpoint ep); - void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz); - void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void SetSocketType(SAMSocketType socketType) { m_SocketType = socketType; }; - void ProcessSessionCreate (char * buf, size_t len); - void ProcessStreamConnect (char * buf, size_t len, size_t rem); - void ProcessStreamAccept (char * buf, size_t len); - void ProcessStreamForward (char * buf, size_t len); - void ProcessDestGenerate (char * buf, size_t len); - void ProcessNamingLookup (char * buf, size_t len); - void ProcessSessionAdd (char * buf, size_t len); - void ProcessSessionRemove (char * buf, size_t len); - void SendI2PError(const std::string & msg); - size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 - void ExtractParams (char * buf, std::map& params); + SAMSocketType GetSocketType() const { return m_SocketType; }; - void Connect (std::shared_ptr remote, std::shared_ptr session = nullptr); - void HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet); - void SendNamingLookupReply (const std::string& name, std::shared_ptr identity); - void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name); - void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); - void SendSessionCreateReplyOk (); + void Terminate(const char *reason); - void WriteI2PData(size_t sz); - void WriteI2PDataImmediate(uint8_t * ptr, size_t sz); + bool IsSession(const std::string &id) const; - void HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff); - void HandleStreamSend(const boost::system::error_code & ec); + private: - private: + void TerminateClose() { Terminate(nullptr); } - SAMBridge& m_Owner; - Socket_t m_Socket; - boost::asio::deadline_timer m_Timer; - char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; - size_t m_BufferOffset; - uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE]; - SAMSocketType m_SocketType; - std::string m_ID; // nickname - bool m_IsSilent; - bool m_IsAccepting; // for eSAMSocketTypeAcceptor only - std::shared_ptr m_Stream; - }; + void HandleHandshakeReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred); - enum SAMSessionType - { - eSAMSessionTypeUnknown, - eSAMSessionTypeStream, - eSAMSessionTypeDatagram, - eSAMSessionTypeRaw, - eSAMSessionTypeMaster - }; + void HandleHandshakeReplySent(const boost::system::error_code &ecode, std::size_t bytes_transferred); - struct SAMSession - { - SAMBridge & m_Bridge; - std::string Name; - SAMSessionType Type; - std::shared_ptr UDPEndpoint; // TODO: move + void HandleMessage(const boost::system::error_code &ecode, std::size_t bytes_transferred); - SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type); - virtual ~SAMSession () {}; + void SendMessageReply(const char *msg, size_t len, bool close); - virtual std::shared_ptr GetLocalDestination () = 0; - virtual void StopLocalDestination () = 0; - virtual void Close () { CloseStreams (); }; + void + HandleMessageReplySent(const boost::system::error_code &ecode, std::size_t bytes_transferred, bool close); - void CloseStreams (); - }; + void Receive(); - struct SAMSingleSession: public SAMSession - { - std::shared_ptr localDestination; + void HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred); - SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr dest); - ~SAMSingleSession (); + void I2PReceive(); - std::shared_ptr GetLocalDestination () { return localDestination; }; - void StopLocalDestination (); - }; + void HandleI2PReceive(const boost::system::error_code &ecode, std::size_t bytes_transferred); - struct SAMMasterSession: public SAMSingleSession - { - std::set subsessions; - SAMMasterSession (SAMBridge & parent, const std::string & name, std::shared_ptr dest): - SAMSingleSession (parent, name, eSAMSessionTypeMaster, dest) {}; - void Close (); - }; + void HandleI2PAccept(std::shared_ptr stream); - struct SAMSubSession: public SAMSession - { - std::shared_ptr masterSession; - int inPort; + void HandleI2PForward(std::shared_ptr stream, boost::asio::ip::tcp::endpoint ep); - SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, int port); - // implements SAMSession - std::shared_ptr GetLocalDestination (); - void StopLocalDestination (); - }; + void HandleWriteI2PData(const boost::system::error_code &ecode, size_t sz); - class SAMBridge: private i2p::util::RunnableService - { - public: + void HandleI2PDatagramReceive(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort, + const uint8_t *buf, size_t len); - SAMBridge (const std::string& address, int port, bool singleThread); - ~SAMBridge (); + void HandleI2PRawDatagramReceive(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len); - void Start (); - void Stop (); + void ProcessSessionCreate(char *buf, size_t len); - boost::asio::io_service& GetService () { return GetIOService (); }; - std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient - const std::map * params); - bool AddSession (std::shared_ptr session); - void CloseSession (const std::string& id); - std::shared_ptr FindSession (const std::string& id) const; + void ProcessStreamConnect(char *buf, size_t len, size_t rem); - std::list > ListSockets(const std::string & id) const; + void ProcessStreamAccept(char *buf, size_t len); - /** send raw data to remote endpoint from our UDP Socket */ - void SendTo (const std::vector& bufs, const boost::asio::ip::udp::endpoint& ep); + void ProcessStreamForward(char *buf, size_t len); - void AddSocket(std::shared_ptr socket); - void RemoveSocket(const std::shared_ptr & socket); + void ProcessDestGenerate(char *buf, size_t len); - bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const; + void ProcessNamingLookup(char *buf, size_t len); - private: + void ProcessSessionAdd(char *buf, size_t len); - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void ProcessSessionRemove(char *buf, size_t len); - void ReceiveDatagram (); - void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendI2PError(const std::string &msg); - private: + size_t ProcessDatagramSend(char *buf, size_t len, const char *data); // from SAM 1.0 + void ExtractParams(char *buf, std::map ¶ms); - bool m_IsSingleThread; - boost::asio::ip::tcp::acceptor m_Acceptor; - boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; - boost::asio::ip::udp::socket m_DatagramSocket; - mutable std::mutex m_SessionsMutex; - std::map > m_Sessions; - mutable std::mutex m_OpenSocketsMutex; - std::list > m_OpenSockets; - uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; - std::map m_SignatureTypes; + void + Connect(std::shared_ptr remote, std::shared_ptr session = nullptr); - public: + void HandleConnectLeaseSetRequestComplete(std::shared_ptr leaseSet); - // for HTTP - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; -} + void SendNamingLookupReply(const std::string &name, std::shared_ptr identity); + + void + HandleNamingLookupLeaseSetRequestComplete(std::shared_ptr leaseSet, std::string name); + + void HandleSessionReadinessCheckTimer(const boost::system::error_code &ecode); + + void SendSessionCreateReplyOk(); + + void WriteI2PData(size_t sz); + + void WriteI2PDataImmediate(uint8_t *ptr, size_t sz); + + void HandleWriteI2PDataImmediate(const boost::system::error_code &ec, uint8_t *buff); + + void HandleStreamSend(const boost::system::error_code &ec); + + private: + + SAMBridge &m_Owner; + Socket_t m_Socket; + boost::asio::deadline_timer m_Timer; + char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; + size_t m_BufferOffset; + uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE]; + SAMSocketType m_SocketType; + std::string m_ID; // nickname + bool m_IsSilent; + bool m_IsAccepting; // for eSAMSocketTypeAcceptor only + std::shared_ptr m_Stream; + }; + + enum SAMSessionType { + eSAMSessionTypeUnknown, + eSAMSessionTypeStream, + eSAMSessionTypeDatagram, + eSAMSessionTypeRaw, + eSAMSessionTypeMaster + }; + + struct SAMSession { + SAMBridge &m_Bridge; + std::string Name; + SAMSessionType Type; + std::shared_ptr UDPEndpoint; // TODO: move + + SAMSession(SAMBridge &parent, const std::string &name, SAMSessionType type); + + virtual ~SAMSession() {}; + + virtual std::shared_ptr GetLocalDestination() = 0; + + virtual void StopLocalDestination() = 0; + + virtual void Close() { CloseStreams(); }; + + void CloseStreams(); + }; + + struct SAMSingleSession : public SAMSession { + std::shared_ptr localDestination; + + SAMSingleSession(SAMBridge &parent, const std::string &name, SAMSessionType type, + std::shared_ptr dest); + + ~SAMSingleSession(); + + std::shared_ptr GetLocalDestination() { return localDestination; }; + + void StopLocalDestination(); + }; + + struct SAMMasterSession : public SAMSingleSession { + std::set subsessions; + + SAMMasterSession(SAMBridge &parent, const std::string &name, std::shared_ptr dest) : + SAMSingleSession(parent, name, eSAMSessionTypeMaster, dest) {}; + + void Close(); + }; + + struct SAMSubSession : public SAMSession { + std::shared_ptr masterSession; + int inPort; + + SAMSubSession(std::shared_ptr master, const std::string &name, SAMSessionType type, + int port); + + // implements SAMSession + std::shared_ptr GetLocalDestination(); + + void StopLocalDestination(); + }; + + class SAMBridge : private i2p::util::RunnableService { + public: + + SAMBridge(const std::string &address, int port, bool singleThread); + + ~SAMBridge(); + + void Start(); + + void Stop(); + + boost::asio::io_service &GetService() { return GetIOService(); }; + + std::shared_ptr CreateSession(const std::string &id, SAMSessionType type, + const std::string &destination, // empty string means transient + const std::map *params); + + bool AddSession(std::shared_ptr session); + + void CloseSession(const std::string &id); + + std::shared_ptr FindSession(const std::string &id) const; + + std::list > ListSockets(const std::string &id) const; + + /** send raw data to remote endpoint from our UDP Socket */ + void SendTo(const std::vector &bufs, const boost::asio::ip::udp::endpoint &ep); + + void AddSocket(std::shared_ptr socket); + + void RemoveSocket(const std::shared_ptr &socket); + + bool ResolveSignatureType(const std::string &name, i2p::data::SigningKeyType &type) const; + + private: + + void Accept(); + + void HandleAccept(const boost::system::error_code &ecode, std::shared_ptr socket); + + void ReceiveDatagram(); + + void HandleReceivedDatagram(const boost::system::error_code &ecode, std::size_t bytes_transferred); + + private: + + bool m_IsSingleThread; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; + boost::asio::ip::udp::socket m_DatagramSocket; + mutable std::mutex m_SessionsMutex; + std::map > m_Sessions; + mutable std::mutex m_OpenSocketsMutex; + std::list > m_OpenSockets; + uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE + 1]; + std::map m_SignatureTypes; + + public: + + // for HTTP + const decltype(m_Sessions) + & + + GetSessions() const { return m_Sessions; }; + }; + } } #endif diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index 961ff934..59837da6 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -20,794 +20,800 @@ #include "I2PService.h" #include "util.h" -namespace i2p -{ -namespace proxy -{ - static const size_t socks_buffer_size = 8192; - static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse +namespace i2p { + namespace proxy { + static const size_t socks_buffer_size = 8192; + static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse - static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192; + static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192; - static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8; + static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8; - struct SOCKSDnsAddress - { - uint8_t size; - char value[max_socks_hostname_size]; - void FromString (const std::string& str) - { - size = str.length(); - if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size; - memcpy(value,str.c_str(),size); - } - std::string ToString() { return std::string(value, size); } - void push_back (char c) { value[size++] = c; } - }; + struct SOCKSDnsAddress { + uint8_t size; + char value[max_socks_hostname_size]; - class SOCKSServer; - class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this - { - private: + void FromString(const std::string &str) { + size = str.length(); + if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size; + memcpy(value, str.c_str(), size); + } - enum state - { - GET_SOCKSV, - GET_COMMAND, - GET_PORT, - GET_IPV4, - GET4_IDENT, - GET4A_HOST, - GET5_AUTHNUM, - GET5_AUTH, - GET5_REQUESTV, - GET5_GETRSV, - GET5_GETADDRTYPE, - GET5_IPV6, - GET5_HOST_SIZE, - GET5_HOST, - READY, - UPSTREAM_RESOLVE, - UPSTREAM_CONNECT, - UPSTREAM_HANDSHAKE - }; - enum authMethods - { - AUTH_NONE = 0, //No authentication, skip to next step - AUTH_GSSAPI = 1, //GSSAPI authentication - AUTH_USERPASSWD = 2, //Username and password - AUTH_UNACCEPTABLE = 0xff //No acceptable method found - }; - enum addrTypes - { - ADDR_IPV4 = 1, //IPv4 address (4 octets) - ADDR_DNS = 3, // DNS name (up to 255 octets) - ADDR_IPV6 = 4 //IPV6 address (16 octets) - }; - enum errTypes - { - SOCKS5_OK = 0, // No error for SOCKS5 - SOCKS5_GEN_FAIL = 1, // General server failure - SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset - SOCKS5_NET_UNREACH = 3, // Network unreachable - SOCKS5_HOST_UNREACH = 4, // Host unreachable - SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer - SOCKS5_TTL_EXPIRED = 6, // TTL Expired - SOCKS5_CMD_UNSUP = 7, // Command unsupported - SOCKS5_ADDR_UNSUP = 8, // Address type unsupported - SOCKS4_OK = 90, // No error for SOCKS4 - SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed - SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server - SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ - }; - enum cmdTypes - { - CMD_CONNECT = 1, // TCP Connect - CMD_BIND = 2, // TCP Bind - CMD_UDP = 3 // UDP associate - }; - enum socksVersions - { - SOCKS4 = 4, // SOCKS4 - SOCKS5 = 5 // SOCKS5 - }; - union address - { - uint32_t ip; - SOCKSDnsAddress dns; - uint8_t ipv6[16]; - }; + std::string ToString() { return std::string(value, size); } - void EnterState(state nstate, uint8_t parseleft = 1); - bool HandleData(uint8_t *sock_buff, std::size_t len); - bool ValidateSOCKSRequest(); - void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void Terminate(); - void AsyncSockRead(); - boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); - boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); - boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); - boost::asio::const_buffers_1 GenerateUpstreamRequest(); - bool Socks5ChooseAuth(); - void SocksRequestFailed(errTypes error); - void SocksRequestSuccess(); - void SentSocksFailed(const boost::system::error_code & ecode); - void SentSocksDone(const boost::system::error_code & ecode); - void SentSocksResponse(const boost::system::error_code & ecode); - void HandleStreamRequestComplete (std::shared_ptr stream); - void ForwardSOCKS(); + void push_back(char c) { value[size++] = c; } + }; - void SocksUpstreamSuccess(); - void AsyncUpstreamSockRead(); - void SendUpstreamRequest(); - void HandleUpstreamData(uint8_t * buff, std::size_t len); - void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void HandleUpstreamConnected(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::iterator itr); - void HandleUpstreamResolved(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::iterator itr); + class SOCKSServer; - boost::asio::ip::tcp::resolver m_proxy_resolver; - uint8_t m_sock_buff[socks_buffer_size]; - std::shared_ptr m_sock, m_upstreamSock; - std::shared_ptr m_stream; - uint8_t *m_remaining_data; //Data left to be sent - uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded - uint8_t m_response[7+max_socks_hostname_size]; - uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE]; - uint8_t m_upstream_request[14+max_socks_hostname_size]; - std::size_t m_upstream_response_len; - address m_address; //Address - std::size_t m_remaining_data_len; //Size of the data left to be sent - uint32_t m_4aip; //Used in 4a requests - uint16_t m_port; - uint8_t m_command; - uint8_t m_parseleft; //Octets left to parse - authMethods m_authchosen; //Authentication chosen - addrTypes m_addrtype; //Address type chosen - socksVersions m_socksv; //Socks version - cmdTypes m_cmd; // Command requested - state m_state; - const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses? - const std::string m_UpstreamProxyAddress; - const uint16_t m_UpstreamProxyPort; + class SOCKSHandler : public i2p::client::I2PServiceHandler, public std::enable_shared_from_this { + private: - public: + enum state { + GET_SOCKSV, + GET_COMMAND, + GET_PORT, + GET_IPV4, + GET4_IDENT, + GET4A_HOST, + GET5_AUTHNUM, + GET5_AUTH, + GET5_REQUESTV, + GET5_GETRSV, + GET5_GETADDRTYPE, + GET5_IPV6, + GET5_HOST_SIZE, + GET5_HOST, + READY, + UPSTREAM_RESOLVE, + UPSTREAM_CONNECT, + UPSTREAM_HANDSHAKE + }; + enum authMethods { + AUTH_NONE = 0, //No authentication, skip to next step + AUTH_GSSAPI = 1, //GSSAPI authentication + AUTH_USERPASSWD = 2, //Username and password + AUTH_UNACCEPTABLE = 0xff //No acceptable method found + }; + enum addrTypes { + ADDR_IPV4 = 1, //IPv4 address (4 octets) + ADDR_DNS = 3, // DNS name (up to 255 octets) + ADDR_IPV6 = 4 //IPV6 address (16 octets) + }; + enum errTypes { + SOCKS5_OK = 0, // No error for SOCKS5 + SOCKS5_GEN_FAIL = 1, // General server failure + SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset + SOCKS5_NET_UNREACH = 3, // Network unreachable + SOCKS5_HOST_UNREACH = 4, // Host unreachable + SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer + SOCKS5_TTL_EXPIRED = 6, // TTL Expired + SOCKS5_CMD_UNSUP = 7, // Command unsupported + SOCKS5_ADDR_UNSUP = 8, // Address type unsupported + SOCKS4_OK = 90, // No error for SOCKS4 + SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed + SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server + SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ + }; + enum cmdTypes { + CMD_CONNECT = 1, // TCP Connect + CMD_BIND = 2, // TCP Bind + CMD_UDP = 3 // UDP associate + }; + enum socksVersions { + SOCKS4 = 4, // SOCKS4 + SOCKS5 = 5 // SOCKS5 + }; + union address { + uint32_t ip; + SOCKSDnsAddress dns; + uint8_t ipv6[16]; + }; - SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) : - I2PServiceHandler(parent), - m_proxy_resolver(parent->GetService()), - m_sock(sock), m_stream(nullptr), - m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), - m_UseUpstreamProxy(useUpstream), - m_UpstreamProxyAddress(upstreamAddr), - m_UpstreamProxyPort(upstreamPort) - { m_address.ip = 0; EnterState(GET_SOCKSV); } + void EnterState(state nstate, uint8_t parseleft = 1); - ~SOCKSHandler() { Terminate(); } - void Handle() { AsyncSockRead(); } - }; + bool HandleData(uint8_t *sock_buff, std::size_t len); - void SOCKSHandler::AsyncSockRead() - { - LogPrint(eLogDebug, "SOCKS: Async sock read"); - if (m_sock) { - m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), - std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } else { - LogPrint(eLogError,"SOCKS: No socket for read"); - } - } + bool ValidateSOCKSRequest(); - void SOCKSHandler::Terminate() - { - if (Kill()) return; - if (m_sock) - { - LogPrint(eLogDebug, "SOCKS: Closing socket"); - m_sock->close(); - m_sock = nullptr; - } - if (m_upstreamSock) - { - LogPrint(eLogDebug, "SOCKS: Closing upstream socket"); - m_upstreamSock->close(); - m_upstreamSock = nullptr; - } - if (m_stream) - { - LogPrint(eLogDebug, "SOCKS: Closing stream"); - m_stream.reset (); - } - Done(shared_from_this()); - } + void HandleSockRecv(const boost::system::error_code &ecode, std::size_t bytes_transfered); - boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) - { - assert(error >= SOCKS4_OK); - m_response[0] = '\x00'; // version - m_response[1] = error; // response code - htobe16buf(m_response + 2, port); // port - htobe32buf(m_response + 4, ip); // IP - return boost::asio::const_buffers_1(m_response,8); - } + void Terminate(); - boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) - { - size_t size = 6; // header + port - assert(error <= SOCKS5_ADDR_UNSUP); - m_response[0] = '\x05'; // version - m_response[1] = error; // response code - m_response[2] = '\x00'; // reserved - m_response[3] = type; // address type - switch (type) - { - case ADDR_IPV4: - size += 4; - htobe32buf(m_response + 4, addr.ip); - htobe16buf(m_response + size - 2, port); - break; - case ADDR_IPV6: - size += 16; - memcpy(m_response + 4, addr.ipv6, 16); - htobe16buf(m_response + size - 2, port); - break; - case ADDR_DNS: - std::string address(addr.dns.value, addr.dns.size); - if(address.substr(addr.dns.size - 4, 4) == ".i2p") // overwrite if requested address inside I2P - { - m_response[3] = ADDR_IPV4; - size += 4; - memset(m_response + 4, 0, 6); // six HEX zeros - } - else - { - size += (1 + addr.dns.size); /* name length + resolved address */ - m_response[4] = addr.dns.size; - memcpy(m_response + 5, addr.dns.value, addr.dns.size); - htobe16buf(m_response + size - 2, port); - } - break; - } - return boost::asio::const_buffers_1(m_response, size); - } + void AsyncSockRead(); - boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest() - { - size_t upstreamRequestSize = 0; - // TODO: negotiate with upstream - // SOCKS 4a - m_upstream_request[0] = '\x04'; //version - m_upstream_request[1] = m_cmd; - htobe16buf(m_upstream_request + 2, m_port); - m_upstream_request[4] = 0; - m_upstream_request[5] = 0; - m_upstream_request[6] = 0; - m_upstream_request[7] = 1; - // user id - m_upstream_request[8] = 'i'; - m_upstream_request[9] = '2'; - m_upstream_request[10] = 'p'; - m_upstream_request[11] = 'd'; - m_upstream_request[12] = 0; - upstreamRequestSize += 13; - if (m_address.dns.size <= max_socks_hostname_size - ( upstreamRequestSize + 1) ) { - // bounds check okay - memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size); - upstreamRequestSize += m_address.dns.size; - // null terminate - m_upstream_request[++upstreamRequestSize] = 0; - } else { - LogPrint(eLogError, "SOCKS: BUG!!! m_addr.dns.sizs > max_socks_hostname - ( upstreamRequestSize + 1 ) )"); - } - return boost::asio::const_buffers_1(m_upstream_request, upstreamRequestSize); - } + boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); - bool SOCKSHandler::Socks5ChooseAuth() - { - m_response[0] = '\x05'; // Version - m_response[1] = m_authchosen; // Response code - boost::asio::const_buffers_1 response(m_response, 2); - if (m_authchosen == AUTH_UNACCEPTABLE) - { - LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed"); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, shared_from_this(), std::placeholders::_1)); - return false; - } - else - { - LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), std::placeholders::_1)); - return true; - } - } + boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); - /* All hope is lost beyond this point */ - void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) - { - boost::asio::const_buffers_1 response(nullptr,0); - assert(error != SOCKS4_OK && error != SOCKS5_OK); - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogWarning, "SOCKS: v4 request failed: ", error); - if (error < SOCKS4_OK) error = SOCKS4_FAIL; // Transparently map SOCKS5 errors - response = GenerateSOCKS4Response(error, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogWarning, "SOCKS: v5 request failed: ", error); - response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port); - break; - } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, - shared_from_this(), std::placeholders::_1)); - } + boost::asio::const_buffers_1 + GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); - void SOCKSHandler::SocksRequestSuccess() - { - boost::asio::const_buffers_1 response(nullptr,0); - // TODO: this should depend on things like the command type and callbacks may change - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogInfo, "SOCKS: v4 connection success"); - response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogInfo, "SOCKS: v5 connection success"); - auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash()); - address ad; ad.dns.FromString(s); - // HACK only 16 bits passed in port as SOCKS5 doesn't allow for more - response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); - break; - } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, shared_from_this(), std::placeholders::_1)); - } + boost::asio::const_buffers_1 GenerateUpstreamRequest(); - void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { - switch (nstate) - { - case GET_PORT: parseleft = 2; break; - case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break; - case GET4_IDENT: m_4aip = m_address.ip; break; - case GET4A_HOST: - case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break; - case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break; - default:; - } - m_parseleft = parseleft; - m_state = nstate; - } + bool Socks5ChooseAuth(); - bool SOCKSHandler::ValidateSOCKSRequest() - { - if ( m_cmd != CMD_CONNECT ) - { - // TODO: we need to support binds and other shit! - LogPrint(eLogError, "SOCKS: Unsupported command: ", m_cmd); - SocksRequestFailed(SOCKS5_CMD_UNSUP); - return false; - } - // TODO: we may want to support other address types! - if ( m_addrtype != ADDR_DNS ) - { - switch (m_socksv) - { - case SOCKS5: - LogPrint(eLogError, "SOCKS: v5 unsupported address type: ", m_addrtype); - break; - case SOCKS4: - LogPrint(eLogError, "SOCKS: Request with v4a rejected because it's actually SOCKS4"); - break; - } - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - return false; - } - return true; - } + void SocksRequestFailed(errTypes error); - bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) - { - assert(len); // This should always be called with a least a byte left to parse - while (len > 0) - { - switch (m_state) - { - case GET_SOCKSV: - m_socksv = (SOCKSHandler::socksVersions) *sock_buff; - switch (*sock_buff) - { - case SOCKS4: - EnterState(GET_COMMAND); //Initialize the parser at the right position - break; - case SOCKS5: - EnterState(GET5_AUTHNUM); //Initialize the parser at the right position - break; - default: - LogPrint(eLogError, "SOCKS: Rejected invalid version: ", ((int)*sock_buff)); - Terminate(); - return false; - } - break; - case GET5_AUTHNUM: - EnterState(GET5_AUTH, *sock_buff); - break; - case GET5_AUTH: - m_parseleft --; - if (*sock_buff == AUTH_NONE) - m_authchosen = AUTH_NONE; - if ( m_parseleft == 0 ) - { - if (!Socks5ChooseAuth()) return false; - EnterState(GET5_REQUESTV); - } - break; - case GET_COMMAND: - switch (*sock_buff) - { - case CMD_CONNECT: - case CMD_BIND: - break; - case CMD_UDP: - if (m_socksv == SOCKS5) break; + void SocksRequestSuccess(); + + void SentSocksFailed(const boost::system::error_code &ecode); + + void SentSocksDone(const boost::system::error_code &ecode); + + void SentSocksResponse(const boost::system::error_code &ecode); + + void HandleStreamRequestComplete(std::shared_ptr stream); + + void ForwardSOCKS(); + + void SocksUpstreamSuccess(); + + void AsyncUpstreamSockRead(); + + void SendUpstreamRequest(); + + void HandleUpstreamData(uint8_t *buff, std::size_t len); + + void HandleUpstreamSockSend(const boost::system::error_code &ecode, std::size_t bytes_transfered); + + void HandleUpstreamSockRecv(const boost::system::error_code &ecode, std::size_t bytes_transfered); + + void HandleUpstreamConnected(const boost::system::error_code &ecode, + boost::asio::ip::tcp::resolver::iterator itr); + + void HandleUpstreamResolved(const boost::system::error_code &ecode, + boost::asio::ip::tcp::resolver::iterator itr); + + boost::asio::ip::tcp::resolver m_proxy_resolver; + uint8_t m_sock_buff[socks_buffer_size]; + std::shared_ptr m_sock, m_upstreamSock; + std::shared_ptr m_stream; + uint8_t *m_remaining_data; //Data left to be sent + uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded + uint8_t m_response[7 + max_socks_hostname_size]; + uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE]; + uint8_t m_upstream_request[14 + max_socks_hostname_size]; + std::size_t m_upstream_response_len; + address m_address; //Address + std::size_t m_remaining_data_len; //Size of the data left to be sent + uint32_t m_4aip; //Used in 4a requests + uint16_t m_port; + uint8_t m_command; + uint8_t m_parseleft; //Octets left to parse + authMethods m_authchosen; //Authentication chosen + addrTypes m_addrtype; //Address type chosen + socksVersions m_socksv; //Socks version + cmdTypes m_cmd; // Command requested + state m_state; + const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses? + const std::string m_UpstreamProxyAddress; + const uint16_t m_UpstreamProxyPort; + + public: + + SOCKSHandler(SOCKSServer *parent, std::shared_ptr sock, + const std::string &upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) : + I2PServiceHandler(parent), + m_proxy_resolver(parent->GetService()), + m_sock(sock), m_stream(nullptr), + m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), + m_UseUpstreamProxy(useUpstream), + m_UpstreamProxyAddress(upstreamAddr), + m_UpstreamProxyPort(upstreamPort) { + m_address.ip = 0; + EnterState(GET_SOCKSV); + } + + ~SOCKSHandler() { Terminate(); } + + void Handle() { AsyncSockRead(); } + }; + + void SOCKSHandler::AsyncSockRead() { + LogPrint(eLogDebug, "SOCKS: Async sock read"); + if (m_sock) { + m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), + std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } else { + LogPrint(eLogError, "SOCKS: No socket for read"); + } + } + + void SOCKSHandler::Terminate() { + if (Kill()) return; + if (m_sock) { + LogPrint(eLogDebug, "SOCKS: Closing socket"); + m_sock->close(); + m_sock = nullptr; + } + if (m_upstreamSock) { + LogPrint(eLogDebug, "SOCKS: Closing upstream socket"); + m_upstreamSock->close(); + m_upstreamSock = nullptr; + } + if (m_stream) { + LogPrint(eLogDebug, "SOCKS: Closing stream"); + m_stream.reset(); + } + Done(shared_from_this()); + } + + boost::asio::const_buffers_1 + SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) { + assert(error >= SOCKS4_OK); + m_response[0] = '\x00'; // version + m_response[1] = error; // response code + htobe16buf(m_response + 2, port); // port + htobe32buf(m_response + 4, ip); // IP + return boost::asio::const_buffers_1(m_response, 8); + } + + boost::asio::const_buffers_1 + SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, + const SOCKSHandler::address &addr, uint16_t port) { + size_t size = 6; // header + port + assert(error <= SOCKS5_ADDR_UNSUP); + m_response[0] = '\x05'; // version + m_response[1] = error; // response code + m_response[2] = '\x00'; // reserved + m_response[3] = type; // address type + switch (type) { + case ADDR_IPV4: + size += 4; + htobe32buf(m_response + 4, addr.ip); + htobe16buf(m_response + size - 2, port); + break; + case ADDR_IPV6: + size += 16; + memcpy(m_response + 4, addr.ipv6, 16); + htobe16buf(m_response + size - 2, port); + break; + case ADDR_DNS: + std::string address(addr.dns.value, addr.dns.size); + if (address.substr(addr.dns.size - 4, 4) == ".i2p") // overwrite if requested address inside I2P + { + m_response[3] = ADDR_IPV4; + size += 4; + memset(m_response + 4, 0, 6); // six HEX zeros + } else { + size += (1 + addr.dns.size); /* name length + resolved address */ + m_response[4] = addr.dns.size; + memcpy(m_response + 5, addr.dns.value, addr.dns.size); + htobe16buf(m_response + size - 2, port); + } + break; + } + return boost::asio::const_buffers_1(m_response, size); + } + + boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest() { + size_t upstreamRequestSize = 0; + // TODO: negotiate with upstream + // SOCKS 4a + m_upstream_request[0] = '\x04'; //version + m_upstream_request[1] = m_cmd; + htobe16buf(m_upstream_request + 2, m_port); + m_upstream_request[4] = 0; + m_upstream_request[5] = 0; + m_upstream_request[6] = 0; + m_upstream_request[7] = 1; + // user id + m_upstream_request[8] = 'i'; + m_upstream_request[9] = '2'; + m_upstream_request[10] = 'p'; + m_upstream_request[11] = 'd'; + m_upstream_request[12] = 0; + upstreamRequestSize += 13; + if (m_address.dns.size <= max_socks_hostname_size - (upstreamRequestSize + 1)) { + // bounds check okay + memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size); + upstreamRequestSize += m_address.dns.size; + // null terminate + m_upstream_request[++upstreamRequestSize] = 0; + } else { + LogPrint(eLogError, + "SOCKS: BUG!!! m_addr.dns.sizs > max_socks_hostname - ( upstreamRequestSize + 1 ) )"); + } + return boost::asio::const_buffers_1(m_upstream_request, upstreamRequestSize); + } + + bool SOCKSHandler::Socks5ChooseAuth() { + m_response[0] = '\x05'; // Version + m_response[1] = m_authchosen; // Response code + boost::asio::const_buffers_1 response(m_response, 2); + if (m_authchosen == AUTH_UNACCEPTABLE) { + LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed"); + boost::asio::async_write(*m_sock, response, + std::bind(&SOCKSHandler::SentSocksFailed, shared_from_this(), + std::placeholders::_1)); + return false; + } else { + LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen); + boost::asio::async_write(*m_sock, response, + std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), + std::placeholders::_1)); + return true; + } + } + + /* All hope is lost beyond this point */ + void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) { + boost::asio::const_buffers_1 response(nullptr, 0); + assert(error != SOCKS4_OK && error != SOCKS5_OK); + switch (m_socksv) { + case SOCKS4: + LogPrint(eLogWarning, "SOCKS: v4 request failed: ", error); + if (error < SOCKS4_OK) error = SOCKS4_FAIL; // Transparently map SOCKS5 errors + response = GenerateSOCKS4Response(error, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogWarning, "SOCKS: v5 request failed: ", error); + response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port); + break; + } + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, + shared_from_this(), std::placeholders::_1)); + } + + void SOCKSHandler::SocksRequestSuccess() { + boost::asio::const_buffers_1 response(nullptr, 0); + // TODO: this should depend on things like the command type and callbacks may change + switch (m_socksv) { + case SOCKS4: + LogPrint(eLogInfo, "SOCKS: v4 connection success"); + response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogInfo, "SOCKS: v5 connection success"); + auto s = i2p::client::context.GetAddressBook().ToAddress( + GetOwner()->GetLocalDestination()->GetIdentHash()); + address ad; + ad.dns.FromString(s); + // HACK only 16 bits passed in port as SOCKS5 doesn't allow for more + response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); + break; + } + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, shared_from_this(), + std::placeholders::_1)); + } + + void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { + switch (nstate) { + case GET_PORT: + parseleft = 2; + break; + case GET_IPV4: + m_addrtype = ADDR_IPV4; + m_address.ip = 0; + parseleft = 4; + break; + case GET4_IDENT: + m_4aip = m_address.ip; + break; + case GET4A_HOST: + case GET5_HOST: + m_addrtype = ADDR_DNS; + m_address.dns.size = 0; + break; + case GET5_IPV6: + m_addrtype = ADDR_IPV6; + parseleft = 16; + break; + default:; + } + m_parseleft = parseleft; + m_state = nstate; + } + + bool SOCKSHandler::ValidateSOCKSRequest() { + if (m_cmd != CMD_CONNECT) { + // TODO: we need to support binds and other shit! + LogPrint(eLogError, "SOCKS: Unsupported command: ", m_cmd); + SocksRequestFailed(SOCKS5_CMD_UNSUP); + return false; + } + // TODO: we may want to support other address types! + if (m_addrtype != ADDR_DNS) { + switch (m_socksv) { + case SOCKS5: + LogPrint(eLogError, "SOCKS: v5 unsupported address type: ", m_addrtype); + break; + case SOCKS4: + LogPrint(eLogError, "SOCKS: Request with v4a rejected because it's actually SOCKS4"); + break; + } + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + return false; + } + return true; + } + + bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) { + assert(len); // This should always be called with a least a byte left to parse + while (len > 0) { + switch (m_state) { + case GET_SOCKSV: + m_socksv = (SOCKSHandler::socksVersions) *sock_buff; + switch (*sock_buff) { + case SOCKS4: + EnterState(GET_COMMAND); //Initialize the parser at the right position + break; + case SOCKS5: + EnterState(GET5_AUTHNUM); //Initialize the parser at the right position + break; + default: + LogPrint(eLogError, "SOCKS: Rejected invalid version: ", ((int) *sock_buff)); + Terminate(); + return false; + } + break; + case GET5_AUTHNUM: + EnterState(GET5_AUTH, *sock_buff); + break; + case GET5_AUTH: + m_parseleft--; + if (*sock_buff == AUTH_NONE) + m_authchosen = AUTH_NONE; + if (m_parseleft == 0) { + if (!Socks5ChooseAuth()) return false; + EnterState(GET5_REQUESTV); + } + break; + case GET_COMMAND: + switch (*sock_buff) { + case CMD_CONNECT: + case CMD_BIND: + break; + case CMD_UDP: + if (m_socksv == SOCKS5) break; #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; + [[fallthrough]]; #endif - default: - LogPrint(eLogError, "SOCKS: Invalid command: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - m_cmd = (SOCKSHandler::cmdTypes)*sock_buff; - switch (m_socksv) - { - case SOCKS5: EnterState(GET5_GETRSV); break; - case SOCKS4: EnterState(GET_PORT); break; - } - break; - case GET_PORT: - m_port = (m_port << 8)|((uint16_t)*sock_buff); - m_parseleft--; - if (m_parseleft == 0) - { - switch (m_socksv) - { - case SOCKS5: EnterState(READY); break; - case SOCKS4: EnterState(GET_IPV4); break; - } - } - break; - case GET_IPV4: - m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff); - m_parseleft--; - if (m_parseleft == 0) - { - switch (m_socksv) - { - case SOCKS5: EnterState(GET_PORT); break; - case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break; - } - } - break; - case GET4_IDENT: - if (!*sock_buff) - { - if( m_4aip == 0 || m_4aip > 255 ) - EnterState(READY); - else - EnterState(GET4A_HOST); - } - break; - case GET4A_HOST: - if (!*sock_buff) - { - EnterState(READY); - break; - } - if (m_address.dns.size >= max_socks_hostname_size) - { - LogPrint(eLogError, "SOCKS: v4a req failed: destination is too large"); - SocksRequestFailed(SOCKS4_FAIL); - return false; - } - m_address.dns.push_back(*sock_buff); - break; - case GET5_REQUESTV: - if (*sock_buff != SOCKS5) - { - LogPrint(eLogError,"SOCKS: v5 rejected unknown request version: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET_COMMAND); - break; - case GET5_GETRSV: - if ( *sock_buff != 0 ) - { - LogPrint(eLogError, "SOCKS: v5 unknown reserved field: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET5_GETADDRTYPE); - break; - case GET5_GETADDRTYPE: - switch (*sock_buff) - { - case ADDR_IPV4: EnterState(GET_IPV4); break; - case ADDR_IPV6: EnterState(GET5_IPV6); break; - case ADDR_DNS : EnterState(GET5_HOST_SIZE); break; - default: - LogPrint(eLogError, "SOCKS: v5 unknown address type: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - break; - case GET5_IPV6: - m_address.ipv6[16-m_parseleft] = *sock_buff; - m_parseleft--; - if (m_parseleft == 0) EnterState(GET_PORT); - break; - case GET5_HOST_SIZE: - EnterState(GET5_HOST, *sock_buff); - break; - case GET5_HOST: - m_address.dns.push_back(*sock_buff); - m_parseleft--; - if (m_parseleft == 0) EnterState(GET_PORT); - break; - default: - LogPrint(eLogError, "SOCKS: Parse state?? ", m_state); - Terminate(); - return false; - } - sock_buff++; - len--; - if (m_state == READY) - { - m_remaining_data_len = len; - m_remaining_data = sock_buff; - return ValidateSOCKSRequest(); - } - } - return true; - } + default: + LogPrint(eLogError, "SOCKS: Invalid command: ", ((int) *sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + m_cmd = (SOCKSHandler::cmdTypes) *sock_buff; + switch (m_socksv) { + case SOCKS5: + EnterState(GET5_GETRSV); + break; + case SOCKS4: + EnterState(GET_PORT); + break; + } + break; + case GET_PORT: + m_port = (m_port << 8) | ((uint16_t) * sock_buff); + m_parseleft--; + if (m_parseleft == 0) { + switch (m_socksv) { + case SOCKS5: + EnterState(READY); + break; + case SOCKS4: + EnterState(GET_IPV4); + break; + } + } + break; + case GET_IPV4: + m_address.ip = (m_address.ip << 8) | ((uint32_t) * sock_buff); + m_parseleft--; + if (m_parseleft == 0) { + switch (m_socksv) { + case SOCKS5: + EnterState(GET_PORT); + break; + case SOCKS4: + EnterState(GET4_IDENT); + m_4aip = m_address.ip; + break; + } + } + break; + case GET4_IDENT: + if (!*sock_buff) { + if (m_4aip == 0 || m_4aip > 255) + EnterState(READY); + else + EnterState(GET4A_HOST); + } + break; + case GET4A_HOST: + if (!*sock_buff) { + EnterState(READY); + break; + } + if (m_address.dns.size >= max_socks_hostname_size) { + LogPrint(eLogError, "SOCKS: v4a req failed: destination is too large"); + SocksRequestFailed(SOCKS4_FAIL); + return false; + } + m_address.dns.push_back(*sock_buff); + break; + case GET5_REQUESTV: + if (*sock_buff != SOCKS5) { + LogPrint(eLogError, "SOCKS: v5 rejected unknown request version: ", ((int) *sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET_COMMAND); + break; + case GET5_GETRSV: + if (*sock_buff != 0) { + LogPrint(eLogError, "SOCKS: v5 unknown reserved field: ", ((int) *sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET5_GETADDRTYPE); + break; + case GET5_GETADDRTYPE: + switch (*sock_buff) { + case ADDR_IPV4: + EnterState(GET_IPV4); + break; + case ADDR_IPV6: + EnterState(GET5_IPV6); + break; + case ADDR_DNS : + EnterState(GET5_HOST_SIZE); + break; + default: + LogPrint(eLogError, "SOCKS: v5 unknown address type: ", ((int) *sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + break; + case GET5_IPV6: + m_address.ipv6[16 - m_parseleft] = *sock_buff; + m_parseleft--; + if (m_parseleft == 0) EnterState(GET_PORT); + break; + case GET5_HOST_SIZE: + EnterState(GET5_HOST, *sock_buff); + break; + case GET5_HOST: + m_address.dns.push_back(*sock_buff); + m_parseleft--; + if (m_parseleft == 0) EnterState(GET_PORT); + break; + default: + LogPrint(eLogError, "SOCKS: Parse state?? ", m_state); + Terminate(); + return false; + } + sock_buff++; + len--; + if (m_state == READY) { + m_remaining_data_len = len; + m_remaining_data = sock_buff; + return ValidateSOCKSRequest(); + } + } + return true; + } - void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) - { - LogPrint(eLogDebug, "SOCKS: Received ", len, " bytes"); - if(ecode) - { - LogPrint(eLogWarning, "SOCKS: Recv got error: ", ecode); - Terminate(); - return; - } + void SOCKSHandler::HandleSockRecv(const boost::system::error_code &ecode, std::size_t len) { + LogPrint(eLogDebug, "SOCKS: Received ", len, " bytes"); + if (ecode) { + LogPrint(eLogWarning, "SOCKS: Recv got error: ", ecode); + Terminate(); + return; + } - if (HandleData(m_sock_buff, len)) - { - if (m_state == READY) - { - const std::string addr = m_address.dns.ToString(); - LogPrint(eLogInfo, "SOCKS: Requested ", addr, ":" , m_port); - const size_t addrlen = addr.size(); - // does it end with .i2p? - if ( addr.rfind(".i2p") == addrlen - 4) { - // yes it does, make an i2p session - GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port); - } else if (m_UseUpstreamProxy) { - // forward it to upstream proxy - ForwardSOCKS(); - } else { - // no upstream proxy - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - } - } - else - AsyncSockRead(); - } - } + if (HandleData(m_sock_buff, len)) { + if (m_state == READY) { + const std::string addr = m_address.dns.ToString(); + LogPrint(eLogInfo, "SOCKS: Requested ", addr, ":", m_port); + const size_t addrlen = addr.size(); + // does it end with .i2p? + if (addr.rfind(".i2p") == addrlen - 4) { + // yes it does, make an i2p session + GetOwner()->CreateStream(std::bind(&SOCKSHandler::HandleStreamRequestComplete, + shared_from_this(), std::placeholders::_1), + m_address.dns.ToString(), m_port); + } else if (m_UseUpstreamProxy) { + // forward it to upstream proxy + ForwardSOCKS(); + } else { + // no upstream proxy + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + } + } else + AsyncSockRead(); + } + } - void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) - { - if (ecode) - LogPrint (eLogError, "SOCKS: Closing socket after sending failure because: ", ecode.message ()); - Terminate(); - } + void SOCKSHandler::SentSocksFailed(const boost::system::error_code &ecode) { + if (ecode) + LogPrint(eLogError, "SOCKS: Closing socket after sending failure because: ", ecode.message()); + Terminate(); + } - void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode) - { - if (!ecode) - { - if (Kill()) return; - LogPrint (eLogInfo, "SOCKS: New I2PTunnel connection"); - auto connection = std::make_shared(GetOwner(), m_sock, m_stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (m_remaining_data,m_remaining_data_len); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError, "SOCKS: Closing socket after completion reply because: ", ecode.message ()); - Terminate(); - } - } + void SOCKSHandler::SentSocksDone(const boost::system::error_code &ecode) { + if (!ecode) { + if (Kill()) return; + LogPrint(eLogInfo, "SOCKS: New I2PTunnel connection"); + auto connection = std::make_shared(GetOwner(), m_sock, m_stream); + GetOwner()->AddHandler(connection); + connection->I2PConnect(m_remaining_data, m_remaining_data_len); + Done(shared_from_this()); + } else { + LogPrint(eLogError, "SOCKS: Closing socket after completion reply because: ", ecode.message()); + Terminate(); + } + } - void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode) - { - if (ecode) - { - LogPrint (eLogError, "SOCKS: Closing socket after sending reply because: ", ecode.message ()); - Terminate(); - } - } + void SOCKSHandler::SentSocksResponse(const boost::system::error_code &ecode) { + if (ecode) { + LogPrint(eLogError, "SOCKS: Closing socket after sending reply because: ", ecode.message()); + Terminate(); + } + } - void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - m_stream = stream; - SocksRequestSuccess(); - } - else - { - LogPrint (eLogError, "SOCKS: Error when creating the stream, check the previous warnings for more info"); - SocksRequestFailed(SOCKS5_HOST_UNREACH); - } - } + void SOCKSHandler::HandleStreamRequestComplete(std::shared_ptr stream) { + if (stream) { + m_stream = stream; + SocksRequestSuccess(); + } else { + LogPrint(eLogError, "SOCKS: Error when creating the stream, check the previous warnings for more info"); + SocksRequestFailed(SOCKS5_HOST_UNREACH); + } + } - void SOCKSHandler::ForwardSOCKS() - { - LogPrint(eLogInfo, "SOCKS: Forwarding to upstream"); - EnterState(UPSTREAM_RESOLVE); - boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort)); - m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } + void SOCKSHandler::ForwardSOCKS() { + LogPrint(eLogInfo, "SOCKS: Forwarding to upstream"); + EnterState(UPSTREAM_RESOLVE); + boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort)); + m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } - void SOCKSHandler::AsyncUpstreamSockRead() - { - LogPrint(eLogDebug, "SOCKS: Async upstream sock read"); - if (m_upstreamSock) { - m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE), - std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } else { - LogPrint(eLogError, "SOCKS: No upstream socket for read"); - SocksRequestFailed(SOCKS5_GEN_FAIL); - } - } + void SOCKSHandler::AsyncUpstreamSockRead() { + LogPrint(eLogDebug, "SOCKS: Async upstream sock read"); + if (m_upstreamSock) { + m_upstreamSock->async_read_some( + boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE), + std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(), std::placeholders::_1, + std::placeholders::_2)); + } else { + LogPrint(eLogError, "SOCKS: No upstream socket for read"); + SocksRequestFailed(SOCKS5_GEN_FAIL); + } + } - void SOCKSHandler::HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered) - { - if (ecode) { - if (m_state == UPSTREAM_HANDSHAKE ) { - // we are trying to handshake but it failed - SocksRequestFailed(SOCKS5_NET_UNREACH); - } else { - LogPrint(eLogError, "SOCKS: Bad state when reading from upstream: ", (int) m_state); - } - return; - } - HandleUpstreamData(m_upstream_response, bytes_transfered); - } + void + SOCKSHandler::HandleUpstreamSockRecv(const boost::system::error_code &ecode, std::size_t bytes_transfered) { + if (ecode) { + if (m_state == UPSTREAM_HANDSHAKE) { + // we are trying to handshake but it failed + SocksRequestFailed(SOCKS5_NET_UNREACH); + } else { + LogPrint(eLogError, "SOCKS: Bad state when reading from upstream: ", (int) m_state); + } + return; + } + HandleUpstreamData(m_upstream_response, bytes_transfered); + } - void SOCKSHandler::SocksUpstreamSuccess() - { - LogPrint(eLogInfo, "SOCKS: Upstream success"); - boost::asio::const_buffers_1 response(nullptr, 0); - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogInfo, "SOCKS: v4 connection success"); - response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogInfo, "SOCKS: v5 connection success"); - //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more - response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, m_address, m_port); - break; - } - m_sock->send(response); - auto forwarder = std::make_shared(GetOwner(), m_sock, m_upstreamSock); - m_upstreamSock = nullptr; - m_sock = nullptr; - GetOwner()->AddHandler(forwarder); - forwarder->Start(); - Terminate(); + void SOCKSHandler::SocksUpstreamSuccess() { + LogPrint(eLogInfo, "SOCKS: Upstream success"); + boost::asio::const_buffers_1 response(nullptr, 0); + switch (m_socksv) { + case SOCKS4: + LogPrint(eLogInfo, "SOCKS: v4 connection success"); + response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogInfo, "SOCKS: v5 connection success"); + //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more + response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, m_address, m_port); + break; + } + m_sock->send(response); + auto forwarder = std::make_shared(GetOwner(), m_sock, m_upstreamSock); + m_upstreamSock = nullptr; + m_sock = nullptr; + GetOwner()->AddHandler(forwarder); + forwarder->Start(); + Terminate(); - } + } - void SOCKSHandler::HandleUpstreamData(uint8_t * dataptr, std::size_t len) - { - if (m_state == UPSTREAM_HANDSHAKE) { - m_upstream_response_len += len; - // handle handshake data - if (m_upstream_response_len < SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) { - // too small, continue reading - AsyncUpstreamSockRead(); - } else if (len == SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) { - // just right - uint8_t resp = m_upstream_response[1]; - if (resp == SOCKS4_OK) { - // we have connected ! - SocksUpstreamSuccess(); - } else { - // upstream failure - LogPrint(eLogError, "SOCKS: Upstream proxy failure: ", (int) resp); - // TODO: runtime error? - SocksRequestFailed(SOCKS5_GEN_FAIL); - } - } else { - // too big - SocksRequestFailed(SOCKS5_GEN_FAIL); - } - } else { - // invalid state - LogPrint(eLogError, "SOCKS: Invalid state reading from upstream: ", (int) m_state); - } - } + void SOCKSHandler::HandleUpstreamData(uint8_t *dataptr, std::size_t len) { + if (m_state == UPSTREAM_HANDSHAKE) { + m_upstream_response_len += len; + // handle handshake data + if (m_upstream_response_len < SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) { + // too small, continue reading + AsyncUpstreamSockRead(); + } else if (len == SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) { + // just right + uint8_t resp = m_upstream_response[1]; + if (resp == SOCKS4_OK) { + // we have connected ! + SocksUpstreamSuccess(); + } else { + // upstream failure + LogPrint(eLogError, "SOCKS: Upstream proxy failure: ", (int) resp); + // TODO: runtime error? + SocksRequestFailed(SOCKS5_GEN_FAIL); + } + } else { + // too big + SocksRequestFailed(SOCKS5_GEN_FAIL); + } + } else { + // invalid state + LogPrint(eLogError, "SOCKS: Invalid state reading from upstream: ", (int) m_state); + } + } - void SOCKSHandler::SendUpstreamRequest() - { - LogPrint(eLogInfo, "SOCKS: Negotiating with upstream proxy"); - EnterState(UPSTREAM_HANDSHAKE); - if (m_upstreamSock) { - boost::asio::write(*m_upstreamSock, GenerateUpstreamRequest()); - AsyncUpstreamSockRead(); - } else { - LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to"); - } - } + void SOCKSHandler::SendUpstreamRequest() { + LogPrint(eLogInfo, "SOCKS: Negotiating with upstream proxy"); + EnterState(UPSTREAM_HANDSHAKE); + if (m_upstreamSock) { + boost::asio::write(*m_upstreamSock, GenerateUpstreamRequest()); + AsyncUpstreamSockRead(); + } else { + LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to"); + } + } - void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) - { - if (ecode) { - LogPrint(eLogWarning, "SOCKS: Could not connect to upstream proxy: ", ecode.message()); - SocksRequestFailed(SOCKS5_NET_UNREACH); - return; - } - LogPrint(eLogInfo, "SOCKS: Connected to upstream proxy"); - SendUpstreamRequest(); - } + void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code &ecode, + boost::asio::ip::tcp::resolver::iterator itr) { + if (ecode) { + LogPrint(eLogWarning, "SOCKS: Could not connect to upstream proxy: ", ecode.message()); + SocksRequestFailed(SOCKS5_NET_UNREACH); + return; + } + LogPrint(eLogInfo, "SOCKS: Connected to upstream proxy"); + SendUpstreamRequest(); + } - void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) - { - if (ecode) { - // error resolving - LogPrint(eLogWarning, "SOCKS: Upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message()); - SocksRequestFailed(SOCKS5_NET_UNREACH); - return; - } - LogPrint(eLogInfo, "SOCKS: Upstream proxy resolved"); - EnterState(UPSTREAM_CONNECT); - auto & service = GetOwner()->GetService(); - m_upstreamSock = std::make_shared(service); - boost::asio::async_connect(*m_upstreamSock, itr, - std::bind(&SOCKSHandler::HandleUpstreamConnected, - shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } + void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code &ecode, + boost::asio::ip::tcp::resolver::iterator itr) { + if (ecode) { + // error resolving + LogPrint(eLogWarning, "SOCKS: Upstream proxy", m_UpstreamProxyAddress, " not resolved: ", + ecode.message()); + SocksRequestFailed(SOCKS5_NET_UNREACH); + return; + } + LogPrint(eLogInfo, "SOCKS: Upstream proxy resolved"); + EnterState(UPSTREAM_CONNECT); + auto &service = GetOwner()->GetService(); + m_upstreamSock = std::make_shared(service); + boost::asio::async_connect(*m_upstreamSock, itr, + std::bind(&SOCKSHandler::HandleUpstreamConnected, + shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + } - SOCKSServer::SOCKSServer(const std::string& name, const std::string& address, int port, - bool outEnable, const std::string& outAddress, uint16_t outPort, - std::shared_ptr localDestination) : - TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), m_Name (name) - { - m_UseUpstreamProxy = false; - if (outAddress.length() > 0 && outEnable) - SetUpstreamProxy(outAddress, outPort); - } + SOCKSServer::SOCKSServer(const std::string &name, const std::string &address, int port, + bool outEnable, const std::string &outAddress, uint16_t outPort, + std::shared_ptr localDestination) : + TCPIPAcceptor(address, port, + localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination()), + m_Name(name) { + m_UseUpstreamProxy = false; + if (outAddress.length() > 0 && outEnable) + SetUpstreamProxy(outAddress, outPort); + } - std::shared_ptr SOCKSServer::CreateHandler(std::shared_ptr socket) - { - return std::make_shared (this, socket, m_UpstreamProxyAddress, m_UpstreamProxyPort, m_UseUpstreamProxy); - } + std::shared_ptr + SOCKSServer::CreateHandler(std::shared_ptr socket) { + return std::make_shared(this, socket, m_UpstreamProxyAddress, m_UpstreamProxyPort, + m_UseUpstreamProxy); + } - void SOCKSServer::SetUpstreamProxy(const std::string & addr, const uint16_t port) - { - m_UpstreamProxyAddress = addr; - m_UpstreamProxyPort = port; - m_UseUpstreamProxy = true; - } -} + void SOCKSServer::SetUpstreamProxy(const std::string &addr, const uint16_t port) { + m_UpstreamProxyAddress = addr; + m_UpstreamProxyPort = port; + m_UseUpstreamProxy = true; + } + } } diff --git a/libi2pd_client/SOCKS.h b/libi2pd_client/SOCKS.h index f41cfd72..f004c515 100644 --- a/libi2pd_client/SOCKS.h +++ b/libi2pd_client/SOCKS.h @@ -15,35 +15,36 @@ #include #include "I2PService.h" -namespace i2p -{ -namespace proxy -{ - class SOCKSServer: public i2p::client::TCPIPAcceptor - { - public: +namespace i2p { + namespace proxy { + class SOCKSServer : public i2p::client::TCPIPAcceptor { + public: - SOCKSServer(const std::string& name, const std::string& address, int port, bool outEnable, const std::string& outAddress, uint16_t outPort, - std::shared_ptr localDestination = nullptr); - ~SOCKSServer() {}; + SOCKSServer(const std::string &name, const std::string &address, int port, bool outEnable, + const std::string &outAddress, uint16_t outPort, + std::shared_ptr localDestination = nullptr); - void SetUpstreamProxy(const std::string & addr, const uint16_t port); + ~SOCKSServer() {}; - protected: + void SetUpstreamProxy(const std::string &addr, const uint16_t port); - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return m_Name.c_str (); } + protected: - private: + // Implements TCPIPAcceptor + std::shared_ptr + CreateHandler(std::shared_ptr socket); - std::string m_Name; - std::string m_UpstreamProxyAddress; - uint16_t m_UpstreamProxyPort; - bool m_UseUpstreamProxy; - }; + const char *GetName() { return m_Name.c_str(); } - typedef SOCKSServer SOCKSProxy; -} + private: + + std::string m_Name; + std::string m_UpstreamProxyAddress; + uint16_t m_UpstreamProxyPort; + bool m_UseUpstreamProxy; + }; + + typedef SOCKSServer SOCKSProxy; + } } #endif diff --git a/libi2pd_wrapper/api.swigcxx b/libi2pd_wrapper/api.swigcxx index e1d18eef..bd40ecec 100644 --- a/libi2pd_wrapper/api.swigcxx +++ b/libi2pd_wrapper/api.swigcxx @@ -3,6 +3,7 @@ %{ #include "capi.h" + %} %include "capi.h" diff --git a/libi2pd_wrapper/capi.cpp b/libi2pd_wrapper/capi.cpp index af4765da..722fe3c1 100644 --- a/libi2pd_wrapper/capi.cpp +++ b/libi2pd_wrapper/capi.cpp @@ -18,31 +18,26 @@ extern "C" { #endif -void C_InitI2P (int argc, char *argv[], const char * appName) -{ - std::cout << argv; - return i2p::api::InitI2P(argc, argv, appName); +void C_InitI2P(int argc, char *argv[], const char *appName) { + std::cout << argv; + return i2p::api::InitI2P(argc, argv, appName); } -void C_TerminateI2P () -{ - return i2p::api::TerminateI2P(); +void C_TerminateI2P() { + return i2p::api::TerminateI2P(); } -void C_StartI2P () -{ - std::shared_ptr logStream; - return i2p::api::StartI2P(logStream); +void C_StartI2P() { + std::shared_ptr logStream; + return i2p::api::StartI2P(logStream); } -void C_StopI2P () -{ - return i2p::api::StopI2P(); +void C_StopI2P() { + return i2p::api::StopI2P(); } -void C_RunPeerTest () -{ - return i2p::api::RunPeerTest(); +void C_RunPeerTest() { + return i2p::api::RunPeerTest(); } #ifdef __cplusplus diff --git a/libi2pd_wrapper/capi.h b/libi2pd_wrapper/capi.h index aefd89f3..b623e698 100644 --- a/libi2pd_wrapper/capi.h +++ b/libi2pd_wrapper/capi.h @@ -14,13 +14,17 @@ extern "C" { #endif // initialization start and stop -void C_InitI2P (int argc, char *argv[], const char * appName); +void C_InitI2P(int argc, char *argv[], const char *appName); + //void C_InitI2P (int argc, char** argv, const char * appName); -void C_TerminateI2P (); -void C_StartI2P (); +void C_TerminateI2P(); + +void C_StartI2P(); + // write system log to logStream, if not specified to .log in application's folder -void C_StopI2P (); -void C_RunPeerTest (); // should be called after UPnP +void C_StopI2P(); + +void C_RunPeerTest(); // should be called after UPnP #ifdef __cplusplus }