LCOV - code coverage report
Current view: top level - core/core/utils - SPValid.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 309 371 83.3 %
Date: 2024-05-12 00:16:13 Functions: 31 37 83.8 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2019 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPValid.h"
      25             : 
      26             : #include "SPUrl.h"
      27             : 
      28             : #if MODULE_STAPPLER_IDN
      29             : #include "SPIdn.h"
      30             : #else
      31             : 
      32             : namespace STAPPLER_VERSIONIZED stappler::platform {
      33             : 
      34             : size_t makeRandomBytes(uint8_t * buf, size_t count);
      35             : 
      36             : }
      37             : 
      38             : namespace STAPPLER_VERSIONIZED stappler::idn {
      39             : 
      40             : using HostUnicodeChars = chars::Compose<char, chars::CharGroup<char, CharGroupId::Alphanumeric>,
      41             :                 chars::Chars<char, '.', '-'>, chars::Range<char, char(128), char(255)>>;
      42             : 
      43             : using HostAsciiChars = chars::Compose<char, chars::CharGroup<char, CharGroupId::Alphanumeric>,
      44             :                 chars::Chars<char, '.', '-'>>;
      45             : 
      46             : template <typename Interface>
      47             : auto toAscii(StringView source, bool validate = true) -> typename Interface::StringType {
      48             :         if (source.empty()) {
      49             :                 return typename Interface::StringType();
      50             :         }
      51             : 
      52             :         if (validate) {
      53             :                 StringView r(source);
      54             :                 r.skipChars<HostUnicodeChars>();
      55             :                 if (!r.empty()) {
      56             :                         return typename Interface::StringType();
      57             :                 }
      58             :         }
      59             : 
      60             :         return source.str<Interface>();
      61             : }
      62             : 
      63             : template <typename Interface>
      64             : auto toUnicode(StringView source, bool validate = false) -> typename Interface::StringType {
      65             :         if (source.empty()) {
      66             :                 return typename Interface::StringType();
      67             :         }
      68             : 
      69             :         if (validate) {
      70             :                 StringView r(source);
      71             :                 r.skipChars<HostUnicodeChars>();
      72             :                 if (!r.empty()) {
      73             :                         return typename Interface::StringType();
      74             :                 }
      75             :         }
      76             : 
      77             :         return source.str<Interface>();
      78             : }
      79             : 
      80             : }
      81             : 
      82             : #endif
      83             : 
      84             : 
      85             : namespace STAPPLER_VERSIONIZED stappler {
      86             : 
      87             : static bool validateHost(StringView &r);
      88             : 
      89             : }
      90             : 
      91             : namespace STAPPLER_VERSIONIZED stappler::platform {
      92             : 
      93             : size_t makeRandomBytes(uint8_t * buf, size_t count);
      94             : 
      95             : }
      96             : 
      97             : namespace STAPPLER_VERSIONIZED stappler::valid {
      98             : 
      99         650 : inline auto Config_getInternalPasswordKey() { return "Serenity Password Salt"_weak; }
     100             : 
     101             : /** Identifier starts with [a-zA-Z_] and can contain [a-zA-Z0-9_\-.@] */
     102        2675 : bool validateIdentifier(StringView str) {
     103        2675 :         if (str.empty()) {
     104           0 :                 return false;
     105             :         }
     106             : 
     107        2675 :         StringView r(str);
     108        2675 :         if (!r.is<chars::Compose<char, chars::CharGroup<char, CharGroupId::Alphanumeric>, chars::Chars<char, '_'>>>()) {
     109           0 :                 return false;
     110             :         }
     111             : 
     112        2675 :         r.skipChars<chars::CharGroup<char, CharGroupId::Alphanumeric>, chars::Chars<char, '_', '-', '.', '@'>>();
     113        2675 :         if (!r.empty()) {
     114           0 :                 return false;
     115             :         }
     116             : 
     117        2675 :         return true;
     118             : }
     119             : 
     120             : /** Text can contain all characters above 0x1F and \t, \r, \n, \b, \f */
     121        5100 : bool validateText(StringView str) {
     122        5100 :         if (str.empty()) {
     123           0 :                 return false;
     124             :         }
     125             : 
     126             :         // 8 9 10 12 13 - allowed
     127        5100 :         StringView r(str);
     128        5100 :         r.skipUntil<chars::Range<char, 0, 7>, chars::Range<char, 14, 31>, chars::Chars<char, 11>>();
     129        5100 :         if (!r.empty()) {
     130           0 :                 return false;
     131             :         }
     132             : 
     133        5100 :         return true;
     134             : }
     135             : 
     136             : template <typename Interface>
     137         250 : static bool _validateEmailQuotation(StringView &r, typename Interface::StringType *target) {
     138         250 :         ++ r;
     139         250 :         if (target) { target->push_back('"'); }
     140         800 :         while (!r.empty() && !r.is('"')) {
     141         550 :                 auto pos = r.readUntil<StringView::Chars<'"', '\\'>>();
     142         550 :                 if (!pos.empty()) {
     143         550 :                         if (target) { target->append(pos.data(), pos.size()); }
     144             :                 }
     145             : 
     146         550 :                 if (r.is('\\')) {
     147         300 :                         if (target) { target->push_back(r[0]); }
     148         300 :                         ++ r;
     149         300 :                         if (!r.empty()) {
     150         300 :                                 if (target) { target->push_back(r[0]); }
     151         300 :                                 ++ r;
     152             :                         }
     153             :                 }
     154             :         }
     155         250 :         if (r.empty()) {
     156           0 :                 return false;
     157             :         } else {
     158         250 :                 if (target) { target->push_back('"'); }
     159         250 :                 ++ r;
     160         250 :                 return true;
     161             :         }
     162             : }
     163             : 
     164             : template <typename Interface>
     165        1750 : static bool _validateEmailData(StringView r, typename Interface::StringType *target) {
     166             :         using namespace chars;
     167             :         using LocalChars = StringView::Compose<StringView::CharGroup<CharGroupId::Alphanumeric>,
     168             :                         StringView::Chars<'_', '-', '+', '#', '!', '$', '%', '&', '\'', '*', '/', '=', '?', '^', '`', '{', '}', '|', '~' >,
     169             :                         StringView::Range<char(128), char(255)>>;
     170             : 
     171             :         using Whitespace =  CharGroup<char, CharGroupId::WhiteSpace>;
     172             : 
     173        1750 :         r.trimChars<Whitespace>();
     174             : 
     175        1750 :         if (r.is('(')) {
     176          25 :                 r.skipUntil<StringView::Chars<')'>>();
     177          25 :                 if (!r.is(')')) {
     178           0 :                         return false;
     179             :                 }
     180          25 :                 r ++;
     181          25 :                 r.skipChars<Whitespace>();
     182             :         }
     183        1750 :         if (r.is('"')) {
     184         250 :                 if (!_validateEmailQuotation<Interface>(r, target)) {
     185           0 :                         return false;
     186             :                 }
     187             :         }
     188             : 
     189        2825 :         while (!r.empty() && !r.is('@')) {
     190        1825 :                 auto pos = r.readChars<LocalChars>();
     191        1825 :                 if (!pos.empty()) {
     192        1825 :                         if (target) { target->append(pos.data(), pos.size()); }
     193             :                 }
     194             : 
     195        1825 :                 if (r.is('.')) {
     196         350 :                         if (target) { target->push_back('.'); }
     197         350 :                         ++ r;
     198         350 :                         if (r.is('"')) {
     199           0 :                                 if (!_validateEmailQuotation<Interface>(r, target)) {
     200         725 :                                         return false;
     201             :                                 }
     202           0 :                                 if (!r.is('.') && !r.is('@')) {
     203           0 :                                         return false;
     204           0 :                                 } else if (r.is('.')) {
     205           0 :                                         if (target) { target->push_back('.'); }
     206           0 :                                         ++ r;
     207             :                                 }
     208         350 :                         } else if (!r.is<LocalChars>()) {
     209          25 :                                 return false;
     210             :                         }
     211             :                 }
     212        1800 :                 if (r.is('(')) {
     213          25 :                         r.skipUntil<StringView::Chars<')'>>();
     214          25 :                         if (!r.is(')')) {
     215           0 :                                 return false;
     216             :                         }
     217          25 :                         r ++;
     218          25 :                         r.skipChars<Whitespace>();
     219          25 :                         break;
     220             :                 }
     221        1775 :                 if (!r.is('@') && !r.is<LocalChars>()) {
     222         700 :                         return false;
     223             :                 }
     224             :         }
     225             : 
     226        1025 :         if (r.empty() || !r.is('@')) {
     227           0 :                 return false;
     228             :         }
     229             : 
     230        1025 :         if (target) { target->push_back('@'); }
     231        1025 :         ++ r;
     232        1025 :         if (r.is('(')) {
     233          25 :                 r.skipUntil<StringView::Chars<')'>>();
     234          25 :                 if (!r.is(')')) {
     235           0 :                         return false;
     236             :                 }
     237          25 :                 r ++;
     238          25 :                 r.skipChars<Whitespace>();
     239             :         }
     240             : 
     241        1025 :         if (r.is('[')) {
     242          50 :                 if (target) { target->push_back('['); }
     243          50 :                 auto pos = r.readUntil<StringView::Chars<']'>>();
     244          50 :                 if (r.is(']')) {
     245          50 :                         r ++;
     246          50 :                         if (r.empty()) {
     247          50 :                                 if (target) { target->append(pos.data(), pos.size()); }
     248          50 :                                 if (target) { target->push_back(']'); }
     249             :                         }
     250             :                 }
     251             :         } else {
     252         975 :                 if (!validateHost(r)) {
     253         100 :                         return false;
     254             :                 }
     255             : 
     256         875 :                 auto host = idn::toAscii<Interface>(r, false);
     257         875 :                 if (host.empty()) {
     258           0 :                         return false;
     259             :                 }
     260             : 
     261         875 :                 if (target) { target->append(host); }
     262             : 
     263             :                 /*while (!tmp.empty()) {
     264             :                         auto t = tmp.readUntil<StringView::Chars<'.'>>();
     265             :                         if (tmp.is('.')) {
     266             :                                 if (t.empty()) {
     267             :                                         return false;
     268             :                                 }
     269             :                                 ++ tmp;
     270             :                         }
     271             :                 }*/
     272             : 
     273         875 :         }
     274             : 
     275         925 :         return true;
     276             : }
     277             : 
     278             : template <typename Interface>
     279        1275 : static bool _validateEmail(typename Interface::StringType &istr) {
     280        1275 :         StringView str(istr);
     281        1275 :         str.trimChars<StringView::WhiteSpace>();
     282        1275 :         if (str.empty()) {
     283           0 :                 return false;
     284             :         }
     285             : 
     286        1275 :         if (str.back() == ')') {
     287          25 :                 auto pos = str.rfind('(');
     288          25 :                 if (pos != Interface::StringType::npos) {
     289          25 :                         str = str.sub(0, pos);
     290             :                 } else {
     291           0 :                         return false;
     292             :                 }
     293             :         }
     294             : 
     295        1275 :         typename Interface::StringType ret; ret.reserve(str.size());
     296        1275 :         if (_validateEmailData<Interface>(str, &ret)) {
     297         500 :                 istr = std::move(ret);
     298         500 :                 return true;
     299             :         }
     300         775 :         return false;
     301        1275 : }
     302             : 
     303         475 : bool validateEmailWithoutNormalization(StringView str) {
     304         475 :         return _validateEmailData<memory::PoolInterface>(str, nullptr);
     305             : }
     306             : 
     307        1275 : bool validateEmail(memory::PoolInterface::StringType &str) {
     308        1275 :         return _validateEmail<memory::PoolInterface>(str);
     309             : }
     310             : 
     311           0 : bool validateEmail(memory::StandartInterface::StringType &str) {
     312           0 :         return _validateEmail<memory::StandartInterface>(str);
     313             : }
     314             : 
     315             : template <typename Interface>
     316        1050 : static bool _validateUrl(typename Interface::StringType &str) {
     317        1050 :         UrlView url;
     318        1050 :         if (!url.parse(str)) {
     319           0 :                 return false;
     320             :         }
     321             : 
     322        1050 :         auto oldHost = url.host;
     323        1050 :         if (url.host.empty() && url.path.size() < 2) {
     324           0 :                 return false;
     325             :         }
     326             : 
     327        1050 :         if (!oldHost.empty()) {
     328        1000 :                 auto str = oldHost.str<Interface>();
     329        1000 :                 auto newHost = idn::toAscii<Interface>(str, true);
     330        1000 :                 if (newHost.empty()) {
     331           0 :                         return false;
     332             :                 }
     333        1000 :                 url.host = newHost;
     334        1000 :         }
     335             : 
     336        1050 :         auto newUrl = url.get<Interface>();
     337        1050 :         str = typename Interface::StringType(newUrl.data(), newUrl.size());
     338        1050 :         return true;
     339        1050 : }
     340             : 
     341         825 : bool validateUrl(memory::PoolInterface::StringType &str) {
     342         825 :         return _validateUrl<memory::PoolInterface>(str);
     343             : }
     344             : 
     345         225 : bool validateUrl(memory::StandartInterface::StringType &str) {
     346         225 :         return _validateUrl<memory::StandartInterface>(str);
     347             : }
     348             : 
     349        1450 : bool validateNumber(const StringView &str) {
     350        1450 :         if (str.empty()) {
     351           0 :                 return false;
     352             :         }
     353             : 
     354        1450 :         StringView r(str);
     355        1450 :         if (r.is('-')) { ++ r; }
     356        1450 :         r.skipChars<chars::Range<char, '0', '9'>>();
     357        1450 :         if (!r.empty()) {
     358         425 :                 return false;
     359             :         }
     360             : 
     361        1025 :         return true;
     362             : }
     363             : 
     364           0 : bool validateHexadecimial(const StringView &str) {
     365           0 :         if (str.empty()) {
     366           0 :                 return false;
     367             :         }
     368             : 
     369           0 :         StringView r(str);
     370           0 :         r.skipChars<chars::CharGroup<char, CharGroupId::Hexadecimial>>();
     371           0 :         if (!r.empty()) {
     372           0 :                 return false;
     373             :         }
     374             : 
     375           0 :         return true;
     376             : }
     377             : 
     378         175 : bool validateBase64(const StringView &str) {
     379         175 :         if (str.empty()) {
     380           0 :                 return false;
     381             :         }
     382             : 
     383         175 :         StringView r(str);
     384         175 :         r.skipChars<chars::CharGroup<char, CharGroupId::Base64>>();
     385         175 :         if (!r.empty()) {
     386           0 :                 return false;
     387             :         }
     388             : 
     389         175 :         return true;
     390             : }
     391             : 
     392        2957 : void makeRandomBytes_buf(uint8_t * buf, size_t count) {
     393        2957 :         auto offset = platform::makeRandomBytes(buf, count);
     394             : 
     395        2957 :         buf += offset;
     396        2957 :         count -= offset;
     397             : 
     398        2957 :         if (count == 0) {
     399           0 :                 return;
     400             :         }
     401             : 
     402        2957 :         auto bytesInRand = 0;
     403        2957 :         size_t tmp = RAND_MAX;
     404       11828 :         while (tmp > 255) {
     405        8871 :                 ++ bytesInRand;
     406        8871 :                 tmp /= 256;
     407             :         }
     408             : 
     409        2957 :         size_t b = bytesInRand;
     410        2957 :         size_t r = rand();
     411       45419 :         for (size_t i = 0; i < count; i++) {
     412       42462 :                 buf[i] = (uint8_t)(r % 256);
     413       42462 :                 r /= 256;
     414       42462 :                 -- b;
     415       42462 :                 if (b == 0) {
     416       13910 :                         b = bytesInRand;
     417       13910 :                         r = rand();
     418             :                 }
     419             :         }
     420             : }
     421             : 
     422          25 : void makeRandomBytes(uint8_t *buf, size_t count) {
     423          25 :         makeRandomBytes_buf(buf, count);
     424          25 : }
     425             : 
     426             : template <>
     427        2375 : auto makeRandomBytes<memory::PoolInterface>(size_t count) -> memory::PoolInterface::BytesType  {
     428        2375 :         memory::PoolInterface::BytesType ret; ret.resize(count);
     429        2375 :         makeRandomBytes_buf(ret.data(), count);
     430        2375 :         return ret;
     431           0 : }
     432             : 
     433             : template <>
     434         457 : auto makeRandomBytes<memory::StandartInterface>(size_t count) -> memory::StandartInterface::BytesType  {
     435         457 :         memory::StandartInterface::BytesType ret; ret.resize(count);
     436         457 :         makeRandomBytes_buf(ret.data(), count);
     437         457 :         return ret;
     438           0 : }
     439             : 
     440         100 : static void makePassword_buf(uint8_t *passwdKey, const StringView &str, const StringView &key) {
     441         100 :         string::Sha512::Buf source = string::Sha512::make(str, Config_getInternalPasswordKey());
     442             : 
     443         100 :         passwdKey[0] = 0; passwdKey[1] = 1; // version code
     444         100 :         makeRandomBytes_buf(passwdKey + 2, 14);
     445             : 
     446         100 :         string::Sha512 hash_ctx;
     447         100 :         hash_ctx.update(passwdKey, 16);
     448         100 :         if (!key.empty()) {
     449         100 :                 hash_ctx.update(key);
     450             :         }
     451         100 :         hash_ctx.update(source);
     452         100 :         hash_ctx.final(passwdKey + 16);
     453         100 : }
     454             : 
     455             : template <>
     456         100 : auto makePassword<memory::PoolInterface>(const StringView &str, const StringView &key) -> memory::PoolInterface::BytesType {
     457         100 :         if (str.empty() || key.empty()) {
     458           0 :                 return memory::PoolInterface::BytesType();
     459             :         }
     460             : 
     461         100 :         memory::PoolInterface::BytesType passwdKey; passwdKey.resize(16 + string::Sha512::Length);
     462         100 :         makePassword_buf(passwdKey.data(), str, key);
     463         100 :         return passwdKey;
     464         100 : }
     465             : 
     466             : template <>
     467           0 : auto makePassword<memory::StandartInterface>(const StringView &str, const StringView &key) -> memory::StandartInterface::BytesType {
     468           0 :         if (str.empty() || key.empty()) {
     469           0 :                 return memory::StandartInterface::BytesType();
     470             :         }
     471             : 
     472           0 :         memory::StandartInterface::BytesType passwdKey; passwdKey.resize(16 + string::Sha512::Length);
     473           0 :         makePassword_buf(passwdKey.data(), str, key);
     474           0 :         return passwdKey;
     475           0 : }
     476             : 
     477         550 : bool validatePassord(const StringView &str, const BytesView &passwd, const StringView &key) {
     478         550 :         if (passwd.size() < 8 + string::Sha256::Length) {
     479           0 :                 return false; // not a password
     480             :         }
     481             : 
     482             :         // Serenity/2 protocol
     483         550 :         if (passwd.size() != 16 + string::Sha512::Length) {
     484           0 :                 return false;
     485             :         }
     486             : 
     487         550 :         string::Sha512::Buf source = string::Sha512::make(str, Config_getInternalPasswordKey());
     488         550 :         uint8_t controlKey [16 + string::Sha512::Length] = { 0 };
     489         550 :         memcpy(controlKey, passwd.data(), 16);
     490             : 
     491         550 :         string::Sha512 hash_ctx;
     492         550 :         hash_ctx.update(passwd.data(), 16);
     493         550 :         if (!key.empty()) {
     494         550 :                 hash_ctx.update(key);
     495             :         }
     496         550 :         hash_ctx.update(source);
     497         550 :         hash_ctx.final(controlKey + 16);
     498             : 
     499         550 :         if (memcmp(passwd.data() + 16, controlKey + 16, string::Sha512::Length) == 0) {
     500         550 :                 return true;
     501             :         } else {
     502           0 :                 return false;
     503             :         }
     504             : }
     505             : 
     506             : #define PSWD_NUMBERS "12345679"
     507             : #define PSWD_LOWER "abcdefghijkmnopqrstuvwxyz"
     508             : #define PSWD_UPPER "ABCDEFGHJKLMNPQRSTUVWXYZ"
     509             : 
     510             : static const char * const pswd_numbers = PSWD_NUMBERS;
     511             : static const char * const pswd_lower = PSWD_LOWER;
     512             : static const char * const pswd_upper = PSWD_UPPER;
     513             : static const char * const pswd_all = PSWD_NUMBERS PSWD_LOWER PSWD_UPPER;
     514             : 
     515             : static uint8_t pswd_numbersCount = uint8_t(strlen(PSWD_NUMBERS));
     516             : static uint8_t pswd_lowerCount = uint8_t(strlen(PSWD_LOWER));
     517             : static uint8_t pswd_upperCount = uint8_t(strlen(PSWD_UPPER));
     518             : static uint8_t pswd_allCount = uint8_t(strlen(PSWD_NUMBERS PSWD_LOWER PSWD_UPPER));
     519             : 
     520             : 
     521             : template <typename Callback>
     522         100 : static void generatePassword_buf(size_t len, const uint8_t *bytes, const Callback &cb) {
     523         100 :         uint16_t meta = 0;
     524         100 :         memcpy(&meta, bytes, sizeof(uint16_t));
     525             : 
     526         100 :         bool extraChars[3] = { false, false, false };
     527         750 :         for (size_t i = 0; i < len - 3; ++ i) {
     528         650 :                 cb(pswd_all[bytes[i + 5] % pswd_allCount]);
     529         650 :                 if (!extraChars[0] && i == bytes[2] % (len - 3)) {
     530         100 :                         cb(pswd_numbers[meta % pswd_numbersCount]);
     531         100 :                         meta /= pswd_numbersCount;
     532         100 :                         extraChars[0] = true;
     533             :                 }
     534         650 :                 if (!extraChars[1] && i == bytes[3] % (len - 3)) {
     535         100 :                         cb(pswd_lower[meta % pswd_lowerCount]);
     536         100 :                         meta /= pswd_lowerCount;
     537         100 :                         extraChars[1] = true;
     538             :                 }
     539         650 :                 if (!extraChars[2] && i == bytes[4] % (len - 3)) {
     540         100 :                         cb(pswd_upper[meta % pswd_upperCount]);
     541         100 :                         meta /= pswd_upperCount;
     542         100 :                         extraChars[2] = true;
     543             :                 }
     544             :         }
     545         100 : }
     546             : 
     547             : template <>
     548          75 : auto generatePassword<memory::PoolInterface>(size_t len) -> memory::PoolInterface::StringType {
     549          75 :         if (len < 6) {
     550           0 :                 return memory::PoolInterface::StringType();
     551             :         }
     552             : 
     553          75 :         auto bytes = makeRandomBytes<memory::PoolInterface>(len + 2);
     554          75 :         memory::PoolInterface::StringType ret; ret.reserve(len);
     555          75 :         generatePassword_buf(len, bytes.data(), [&] (char c) {
     556         550 :                 ret.push_back(c);
     557         550 :         });
     558          75 :         return ret;
     559          75 : }
     560             : 
     561             : template <>
     562          25 : auto generatePassword<memory::StandartInterface>(size_t len) -> memory::StandartInterface::StringType {
     563          25 :         if (len < 6) {
     564           0 :                 return memory::StandartInterface::StringType();
     565             :         }
     566             : 
     567          25 :         auto bytes = makeRandomBytes<memory::StandartInterface>(len + 2);
     568          25 :         memory::StandartInterface::StringType ret; ret.reserve(len);
     569          25 :         generatePassword_buf(len, bytes.data(), [&] (char c) {
     570         400 :                 ret.push_back(c);
     571         400 :         });
     572          25 :         return ret;
     573          25 : }
     574             : 
     575         100 : uint32_t readIp(StringView r) {
     576         100 :         bool err = false;
     577         200 :         return readIp(r, err);
     578             : }
     579             : 
     580         100 : uint32_t readIp(StringView r, bool &err) {
     581         100 :         uint32_t octets = 0;
     582         100 :         uint32_t ret = 0;
     583         325 :         while (!r.empty() && octets < 4) {
     584         325 :                 auto n = r.readChars<StringView::CharGroup<CharGroupId::Numbers>>();
     585         325 :                 if (!n.empty()) {
     586         325 :                         auto num = n.readInteger(10).get(256);
     587         325 :                         if (num < 256) {
     588         300 :                                 ret = (ret << 8) | uint32_t(num);
     589             :                         } else {
     590          25 :                                 err = true;
     591         100 :                                 return 0;
     592             :                         }
     593             :                 }
     594         300 :                 if (r.is('.') && octets < 3) {
     595         225 :                         ++ r;
     596         225 :                         ++ octets;
     597          75 :                 } else if (octets == 3 && r.empty()) {
     598          25 :                         return ret;
     599             :                 } else {
     600          50 :                         err = true;
     601          50 :                         return 0;
     602             :                 }
     603             :         }
     604           0 :         err = true;
     605           0 :         return 0;
     606             : }
     607             : 
     608         175 : Pair<uint32_t, uint32_t> readIpRange(StringView r) {
     609         175 :         uint32_t start = 0;
     610         175 :         uint32_t end = 0;
     611         175 :         uint32_t mask = 0;
     612             : 
     613         250 :         auto fnReadIp = [] (StringView &r, bool &err) -> uint32_t {
     614         250 :                 uint32_t octets = 0;
     615         250 :                 uint32_t ret = 0;
     616         975 :                 while (!r.empty() && octets < 4) {
     617         975 :                         auto n = r.readChars<StringView::CharGroup<CharGroupId::Numbers>>();
     618         975 :                         if (!n.empty()) {
     619         975 :                                 auto num = n.readInteger(10).get(256);
     620         975 :                                 if (num < 256) {
     621         950 :                                         ret = (ret << 8) | uint32_t(num);
     622             :                                 } else {
     623          25 :                                         err = true;
     624         250 :                                         return 0;
     625             :                                 }
     626             :                         }
     627         950 :                         if (r.is('.') && octets < 3) {
     628         725 :                                 ++ r;
     629         725 :                                 ++ octets;
     630         225 :                         } else if (octets == 3) {
     631         225 :                                 if (r.empty()) {
     632         100 :                                         return ret;
     633         125 :                                 } else if (r.is('/') || r.is('-')) {
     634         125 :                                         return ret;
     635             :                                 }
     636             :                         } else {
     637           0 :                                 err = true;
     638           0 :                                 return 0;
     639             :                         }
     640             :                 }
     641           0 :                 err = true;
     642           0 :                 return 0;
     643             :         };
     644             : 
     645         175 :         bool err = false;
     646         175 :         start = fnReadIp(r, err);
     647         175 :         if (err) {
     648          25 :                 return pair(0, 0);
     649             :         }
     650         150 :         if (r.empty()) {
     651          25 :                 return pair(start, start);
     652         125 :         } else if (r.is('-')) {
     653          25 :                 ++ r;
     654          25 :                 end = fnReadIp(r, err);
     655          25 :                 if (err || !r.empty()) {
     656           0 :                         return pair(0, 0);
     657             :                 } else {
     658          25 :                         return pair(start, end);
     659             :                 }
     660         100 :         } else if (r.is('/')) {
     661         100 :                 ++ r;
     662         100 :                 auto tmp = r;
     663         100 :                 auto n = tmp.readChars<StringView::CharGroup<CharGroupId::Numbers>>();
     664         100 :                 auto num = n.readInteger(10).get(256);
     665         100 :                 if (tmp.is('.') && num < 256) {
     666          50 :                         mask = fnReadIp(r, err);
     667          50 :                         if (err || !r.empty()) {
     668           0 :                                 return pair(0, 0);
     669             :                         }
     670             : 
     671          50 :                         uint32_t i = 0;
     672         450 :                         while ((mask & (1 << i)) == 0) {
     673         400 :                                 ++ i;
     674             :                         }
     675             : 
     676         475 :                         while ((mask & (1 << i)) != 0 && i < 32) {
     677         425 :                                 ++ i;
     678             :                         }
     679             : 
     680          50 :                         if (i != 32) {
     681          25 :                                 return pair(0, 0);
     682             :                         }
     683          50 :                 } else if (num < 32 && tmp.empty()) {
     684          25 :                         mask = maxOf<uint32_t>() << (32 - num);
     685          25 :                         r = tmp;
     686             :                 } else {
     687          25 :                         return pair(0, 0);
     688             :                 }
     689             : 
     690          50 :                 if (!r.empty()) {
     691           0 :                         return pair(0, 0);
     692             :                 }
     693             : 
     694          50 :                 uint32_t netstart = (start & mask); // first ip in subnet
     695          50 :                 uint32_t netend = (netstart | ~mask); // last ip in subnet
     696             : 
     697          50 :                 return pair(netstart, netend);
     698             :         }
     699           0 :         return pair(0, 0);
     700             : }
     701             : 
     702             : }

Generated by: LCOV version 1.14