• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 27456950099

12 Jun 2026 07:59PM UTC coverage: 89.424% (+0.05%) from 89.378%
27456950099

push

github

web-flow
Merge pull request #5663 from randombit/jack/dns-uri-ip-fixes

Bugfixes and enhancements for DNSName, URI, IPv4Address, IPv6Address

111165 of 124312 relevant lines covered (89.42%)

10989620.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.13
/src/lib/utils/parsing.cpp
1
/*
2
* Various string utils and parsing functions
3
* (C) 1999-2007,2013,2014,2015,2018 Jack Lloyd
4
* (C) 2015 Simon Warta (Kullo GmbH)
5
* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
6
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include <botan/internal/parsing.h>
11

12
#include <botan/exceptn.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/loadstor.h>
15
#include <concepts>
16
#include <limits>
17
#include <sstream>
18

19
namespace Botan {
20

21
namespace {
22

23
std::optional<size_t> digit_from_ascii(char c) {
795,339✔
24
   if(c >= '0' && c <= '9') {
795,339✔
25
      return c - '0';
795,230✔
26
   } else {
27
      return {};
28
   }
29
}
30

31
template <std::unsigned_integral T>
32
std::optional<T> parse_decimal_integer(std::string_view input, bool require_canonical) {
378,687✔
33
   if(input.empty() || input.size() > (std::numeric_limits<T>::digits10 + 1)) {
378,687✔
34
      return {};
7✔
35
   }
36

37
   // The canonical encoding of zero is "0"; no other value starts with a zero
38
   if(require_canonical && input.size() > 1 && input.front() == '0') {
378,680✔
39
      return {};
9✔
40
   }
41

42
   T accum = 0;
378,671✔
43

44
   for(const char c : input) {
1,173,899✔
45
      if(const auto digit = digit_from_ascii(c)) {
795,339✔
46
         if(accum > (std::numeric_limits<T>::max() - static_cast<T>(*digit)) / 10) {
795,230✔
47
            return {};
2✔
48
         }
49
         accum = accum * 10 + static_cast<T>(*digit);
795,228✔
50
      } else {
51
         return {};
109✔
52
      }
53
   }
54

55
   return accum;
378,560✔
56
}
57

58
}  // namespace
59

60
std::optional<uint16_t> parse_u16(std::string_view input, bool require_canonical) {
447✔
61
   return parse_decimal_integer<uint16_t>(input, require_canonical);
447✔
62
}
63

64
std::optional<uint32_t> parse_u32(std::string_view input, bool require_canonical) {
377,064✔
65
   return parse_decimal_integer<uint32_t>(input, require_canonical);
377,064✔
66
}
67

68
std::optional<uint64_t> parse_u64(std::string_view input, bool require_canonical) {
1,080✔
69
   return parse_decimal_integer<uint64_t>(input, require_canonical);
1,080✔
70
}
71

72
std::optional<size_t> parse_sz(std::string_view input, bool require_canonical) {
96✔
73
   return parse_decimal_integer<size_t>(input, require_canonical);
96✔
74
}
75

76
uint32_t to_u32bit(std::string_view input) {
375,751✔
77
   if(const auto parsed = parse_u32(input)) {
375,751✔
78
      return *parsed;
375,661✔
79
   } else {
80
      throw Invalid_Argument(fmt("Failed to parse input '{}' as a 32-bit integer", input));
180✔
81
   }
82
}
83

84
/*
85
* Parse a SCAN-style algorithm name
86
*/
87
std::vector<std::string> parse_algorithm_name(std::string_view scan_name) {
19,532✔
88
   if(scan_name.find('(') == std::string::npos && scan_name.find(')') == std::string::npos) {
19,532✔
89
      return {std::string(scan_name)};
36,888✔
90
   }
91

92
   std::string name(scan_name);
1,088✔
93
   std::string substring;
1,088✔
94
   std::vector<std::string> elems;
1,088✔
95
   size_t level = 0;
1,088✔
96

97
   elems.push_back(name.substr(0, name.find('(')));
2,176✔
98
   name = name.substr(name.find('('));
1,088✔
99

100
   for(auto i = name.begin(); i != name.end(); ++i) {
4,433✔
101
      const char c = *i;
4,433✔
102

103
      if(c == '(') {
4,433✔
104
         ++level;
1,088✔
105
      }
106
      if(c == ')') {
4,433✔
107
         if(level == 1 && i == name.end() - 1) {
1,088✔
108
            if(elems.size() == 1) {
1,088✔
109
               elems.push_back(substring.substr(1));
1,756✔
110
            } else {
111
               elems.push_back(substring);
210✔
112
            }
113
            return elems;
1,088✔
114
         }
115

116
         if(level == 0 || (level == 1 && i != name.end() - 1)) {
×
117
            throw Invalid_Algorithm_Name(scan_name);
×
118
         }
119
         --level;
×
120
      }
121

122
      if(c == ',' && level == 1) {
3,345✔
123
         if(elems.size() == 1) {
210✔
124
            elems.push_back(substring.substr(1));
420✔
125
         } else {
126
            elems.push_back(substring);
×
127
         }
128
         substring.clear();
210✔
129
      } else {
130
         substring += c;
6,480✔
131
      }
132
   }
133

134
   if(!substring.empty()) {
×
135
      throw Invalid_Algorithm_Name(scan_name);
×
136
   }
137

138
   return elems;
×
139
}
37,976✔
140

141
std::vector<std::string> split_on(std::string_view str, char delim) {
85,385✔
142
   std::vector<std::string> elems;
85,385✔
143
   if(str.empty()) {
85,385✔
144
      return elems;
145
   }
146

147
   std::string substr;
85,255✔
148
   for(const char c : str) {
1,018,358✔
149
      if(c == delim) {
933,103✔
150
         if(!substr.empty()) {
44,792✔
151
            elems.push_back(substr);
44,753✔
152
         }
153
         substr.clear();
44,792✔
154
      } else {
155
         substr += c;
1,821,414✔
156
      }
157
   }
158

159
   if(substr.empty()) {
85,255✔
160
      throw Invalid_Argument(fmt("Unable to split string '{}", str));
2✔
161
   }
162
   elems.push_back(substr);
85,254✔
163

164
   return elems;
85,254✔
165
}
85,255✔
166

167
/*
168
* Join a string
169
*/
170
std::string string_join(const std::vector<std::string>& strs, char delim) {
19✔
171
   std::ostringstream out;
19✔
172

173
   for(size_t i = 0; i != strs.size(); ++i) {
236✔
174
      if(i != 0) {
217✔
175
         out << delim;
198✔
176
      }
177
      out << strs[i];
217✔
178
   }
179

180
   return out.str();
38✔
181
}
19✔
182

183
std::string tolower_string(std::string_view str) {
7,381✔
184
   // Locale-independent ASCII fold; the only callers (DNS name canonicalization
185
   // for SAN/name-constraints) work on ASCII strings per RFC 1035.
186
   std::string lower(str);
7,381✔
187
   for(char& c : lower) {
41,582✔
188
      if(c >= 'A' && c <= 'Z') {
34,201✔
189
         c = static_cast<char>(c + ('a' - 'A'));
20✔
190
      }
191
   }
192
   return lower;
7,381✔
193
}
194

195
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc