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 : }
|