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

randombit / botan / 11087146043

28 Sep 2024 09:28PM UTC coverage: 92.003% (+0.7%) from 91.274%
11087146043

push

github

web-flow
Create terraform.yml

82959 of 90170 relevant lines covered (92.0%)

9376319.11 hits per line

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

90.32
/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 <algorithm>
16
#include <cctype>
17
#include <limits>
18
#include <sstream>
19

20
namespace Botan {
21

22
uint16_t to_uint16(std::string_view str) {
391✔
23
   const uint32_t x = to_u32bit(str);
391✔
24

25
   if(x >> 16) {
391✔
26
      throw Invalid_Argument("Integer value exceeds 16 bit range");
×
27
   }
28

29
   return static_cast<uint16_t>(x);
391✔
30
}
31

32
uint32_t to_u32bit(std::string_view str_view) {
3,088,810✔
33
   const std::string str(str_view);
3,088,810✔
34

35
   // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
36
   for(const char chr : str) {
7,765,722✔
37
      if(chr < '0' || chr > '9') {
4,677,536✔
38
         throw Invalid_Argument("to_u32bit invalid decimal string '" + str + "'");
1,248✔
39
      }
40
   }
41

42
   const unsigned long int x = std::stoul(str);
3,088,186✔
43

44
   if constexpr(sizeof(unsigned long int) > 4) {
3,088,186✔
45
      // x might be uint64
46
      if(x > std::numeric_limits<uint32_t>::max()) {
3,088,186✔
47
         throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
×
48
      }
49
   }
50

51
   return static_cast<uint32_t>(x);
3,088,186✔
52
}
3,088,186✔
53

54
/*
55
* Parse a SCAN-style algorithm name
56
*/
57
std::vector<std::string> parse_algorithm_name(std::string_view namex) {
23,102✔
58
   if(namex.find('(') == std::string::npos && namex.find(')') == std::string::npos) {
23,102✔
59
      return {std::string(namex)};
43,982✔
60
   }
61

62
   std::string name(namex);
1,111✔
63
   std::string substring;
1,111✔
64
   std::vector<std::string> elems;
1,111✔
65
   size_t level = 0;
1,111✔
66

67
   elems.push_back(name.substr(0, name.find('(')));
2,222✔
68
   name = name.substr(name.find('('));
1,111✔
69

70
   for(auto i = name.begin(); i != name.end(); ++i) {
4,453✔
71
      char c = *i;
4,453✔
72

73
      if(c == '(') {
4,453✔
74
         ++level;
1,111✔
75
      }
76
      if(c == ')') {
4,453✔
77
         if(level == 1 && i == name.end() - 1) {
1,111✔
78
            if(elems.size() == 1) {
1,111✔
79
               elems.push_back(substring.substr(1));
1,814✔
80
            } else {
81
               elems.push_back(substring);
204✔
82
            }
83
            return elems;
1,111✔
84
         }
85

86
         if(level == 0 || (level == 1 && i != name.end() - 1)) {
×
87
            throw Invalid_Algorithm_Name(namex);
×
88
         }
89
         --level;
×
90
      }
91

92
      if(c == ',' && level == 1) {
3,342✔
93
         if(elems.size() == 1) {
204✔
94
            elems.push_back(substring.substr(1));
408✔
95
         } else {
96
            elems.push_back(substring);
×
97
         }
98
         substring.clear();
204✔
99
      } else {
100
         substring += c;
6,480✔
101
      }
102
   }
103

104
   if(!substring.empty()) {
×
105
      throw Invalid_Algorithm_Name(namex);
×
106
   }
107

108
   return elems;
×
109
}
24,213✔
110

111
std::vector<std::string> split_on(std::string_view str, char delim) {
196,858✔
112
   std::vector<std::string> elems;
196,858✔
113
   if(str.empty()) {
196,858✔
114
      return elems;
115
   }
116

117
   std::string substr;
196,758✔
118
   for(auto i = str.begin(); i != str.end(); ++i) {
2,513,331✔
119
      if(*i == delim) {
2,316,573✔
120
         if(!substr.empty()) {
158,081✔
121
            elems.push_back(substr);
158,039✔
122
         }
123
         substr.clear();
158,081✔
124
      } else {
125
         substr += *i;
4,475,065✔
126
      }
127
   }
128

129
   if(substr.empty()) {
196,758✔
130
      throw Invalid_Argument(fmt("Unable to split string '{}", str));
2✔
131
   }
132
   elems.push_back(substr);
196,757✔
133

134
   return elems;
196,757✔
135
}
196,858✔
136

137
/*
138
* Join a string
139
*/
140
std::string string_join(const std::vector<std::string>& strs, char delim) {
17✔
141
   std::ostringstream out;
17✔
142

143
   for(size_t i = 0; i != strs.size(); ++i) {
186✔
144
      if(i != 0) {
169✔
145
         out << delim;
152✔
146
      }
147
      out << strs[i];
169✔
148
   }
149

150
   return out.str();
34✔
151
}
17✔
152

153
/*
154
* Convert a decimal-dotted string to binary IP
155
*/
156
uint32_t string_to_ipv4(std::string_view str) {
12✔
157
   const auto parts = split_on(str, '.');
12✔
158

159
   if(parts.size() != 4) {
12✔
160
      throw Decoding_Error(fmt("Invalid IPv4 string '{}'", str));
×
161
   }
162

163
   uint32_t ip = 0;
164

165
   for(auto part = parts.begin(); part != parts.end(); ++part) {
60✔
166
      uint32_t octet = to_u32bit(*part);
48✔
167

168
      if(octet > 255) {
48✔
169
         throw Decoding_Error(fmt("Invalid IPv4 string '{}'", str));
×
170
      }
171

172
      ip = (ip << 8) | (octet & 0xFF);
48✔
173
   }
174

175
   return ip;
12✔
176
}
12✔
177

178
/*
179
* Convert an IP address to decimal-dotted string
180
*/
181
std::string ipv4_to_string(uint32_t ip) {
4,765✔
182
   std::string str;
4,765✔
183
   uint8_t bits[4];
4,765✔
184
   store_be(ip, bits);
4,765✔
185

186
   for(size_t i = 0; i != 4; ++i) {
23,825✔
187
      if(i > 0) {
19,060✔
188
         str += ".";
14,295✔
189
      }
190
      str += std::to_string(bits[i]);
38,120✔
191
   }
192

193
   return str;
4,765✔
194
}
×
195

196
std::string tolower_string(std::string_view in) {
714✔
197
   std::string s(in);
714✔
198
   for(size_t i = 0; i != s.size(); ++i) {
10,370✔
199
      const int cu = static_cast<unsigned char>(s[i]);
9,656✔
200
      if(std::isalpha(cu)) {
9,656✔
201
         s[i] = static_cast<char>(std::tolower(cu));
8,324✔
202
      }
203
   }
204
   return s;
714✔
205
}
206

207
bool host_wildcard_match(std::string_view issued_, std::string_view host_) {
220✔
208
   const std::string issued = tolower_string(issued_);
220✔
209
   const std::string host = tolower_string(host_);
220✔
210

211
   if(host.empty() || issued.empty()) {
220✔
212
      return false;
213
   }
214

215
   /*
216
   If there are embedded nulls in your issued name
217
   Well I feel bad for you son
218
   */
219
   if(std::count(issued.begin(), issued.end(), char(0)) > 0) {
434✔
220
      return false;
221
   }
222

223
   // If more than one wildcard, then issued name is invalid
224
   const size_t stars = std::count(issued.begin(), issued.end(), '*');
217✔
225
   if(stars > 1) {
217✔
226
      return false;
227
   }
228

229
   // '*' is not a valid character in DNS names so should not appear on the host side
230
   if(std::count(host.begin(), host.end(), '*') != 0) {
426✔
231
      return false;
232
   }
233

234
   // Similarly a DNS name can't end in .
235
   if(host[host.size() - 1] == '.') {
210✔
236
      return false;
237
   }
238

239
   // And a host can't have an empty name component, so reject that
240
   if(host.find("..") != std::string::npos) {
210✔
241
      return false;
242
   }
243

244
   // Exact match: accept
245
   if(issued == host) {
208✔
246
      return true;
247
   }
248

249
   /*
250
   Otherwise it might be a wildcard
251

252
   If the issued size is strictly longer than the hostname size it
253
   couldn't possibly be a match, even if the issued value is a
254
   wildcard. The only exception is when the wildcard ends up empty
255
   (eg www.example.com matches www*.example.com)
256
   */
257
   if(issued.size() > host.size() + 1) {
49✔
258
      return false;
259
   }
260

261
   // If no * at all then not a wildcard, and so not a match
262
   if(stars != 1) {
40✔
263
      return false;
264
   }
265

266
   /*
267
   Now walk through the issued string, making sure every character
268
   matches. When we come to the (singular) '*', jump forward in the
269
   hostname by the corresponding amount. We know exactly how much
270
   space the wildcard takes because it must be exactly `len(host) -
271
   len(issued) + 1 chars`.
272

273
   We also verify that the '*' comes in the leftmost component, and
274
   doesn't skip over any '.' in the hostname.
275
   */
276
   size_t dots_seen = 0;
277
   size_t host_idx = 0;
278

279
   for(size_t i = 0; i != issued.size(); ++i) {
291✔
280
      dots_seen += (issued[i] == '.');
275✔
281

282
      if(issued[i] == '*') {
275✔
283
         // Fail: wildcard can only come in leftmost component
284
         if(dots_seen > 0) {
30✔
285
            return false;
286
         }
287

288
         /*
289
         Since there is only one * we know the tail of the issued and
290
         hostname must be an exact match. In this case advance host_idx
291
         to match.
292
         */
293
         const size_t advance = (host.size() - issued.size() + 1);
23✔
294

295
         if(host_idx + advance > host.size()) {  // shouldn't happen
23✔
296
            return false;
297
         }
298

299
         // Can't be any intervening .s that we would have skipped
300
         if(std::count(host.begin() + host_idx, host.begin() + host_idx + advance, '.') != 0) {
46✔
301
            return false;
302
         }
303

304
         host_idx += advance;
305
      } else {
306
         if(issued[i] != host[host_idx]) {
245✔
307
            return false;
308
         }
309

310
         host_idx += 1;
242✔
311
      }
312
   }
313

314
   // Wildcard issued name must have at least 3 components
315
   if(dots_seen < 2) {
16✔
316
      return false;
3✔
317
   }
318

319
   return true;
320
}
343✔
321

322
}  // 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