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

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 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
   return static_cast<uint16_t>(x);
391✔
29
}
30

31
uint32_t to_u32bit(std::string_view str_view) {
2,203,931✔
32
   const std::string str(str_view);
2,203,931✔
33

34
   // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
35
   for(const char chr : str) {
5,554,496✔
36
      if(chr < '0' || chr > '9') {
3,351,175✔
37
         throw Invalid_Argument("to_u32bit invalid decimal string '" + str + "'");
1,220✔
38
      }
39
   }
40

41
   const unsigned long int x = std::stoul(str);
2,203,321✔
42

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

50
   return static_cast<uint32_t>(x);
2,203,321✔
51
}
2,203,321✔
52

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

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

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

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

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

83
         if(level == 0 || (level == 1 && i != name.end() - 1))
×
84
            throw Invalid_Algorithm_Name(namex);
×
85
         --level;
×
86
      }
87

88
      if(c == ',' && level == 1) {
3,342✔
89
         if(elems.size() == 1)
204✔
90
            elems.push_back(substring.substr(1));
408✔
91
         else
92
            elems.push_back(substring);
×
93
         substring.clear();
204✔
94
      } else
95
         substring += c;
6,480✔
96
   }
97

98
   if(!substring.empty())
×
99
      throw Invalid_Algorithm_Name(namex);
×
100

101
   return elems;
×
102
}
23,819✔
103

104
std::vector<std::string> split_on(std::string_view str, char delim) {
188,283✔
105
   std::vector<std::string> elems;
188,283✔
106
   if(str.empty())
188,283✔
107
      return elems;
108

109
   std::string substr;
188,184✔
110
   for(auto i = str.begin(); i != str.end(); ++i) {
2,407,327✔
111
      if(*i == delim) {
2,219,143✔
112
         if(!substr.empty())
153,818✔
113
            elems.push_back(substr);
153,780✔
114
         substr.clear();
153,818✔
115
      } else
116
         substr += *i;
4,284,468✔
117
   }
118

119
   if(substr.empty()) {
188,184✔
120
      throw Invalid_Argument(fmt("Unable to split string '{}", str));
2✔
121
   }
122
   elems.push_back(substr);
188,183✔
123

124
   return elems;
188,183✔
125
}
188,283✔
126

127
/*
128
* Join a string
129
*/
130
std::string string_join(const std::vector<std::string>& strs, char delim) {
16✔
131
   std::ostringstream out;
16✔
132

133
   for(size_t i = 0; i != strs.size(); ++i) {
160✔
134
      if(i != 0)
144✔
135
         out << delim;
128✔
136
      out << strs[i];
144✔
137
   }
138

139
   return out.str();
32✔
140
}
16✔
141

142
/*
143
* Convert a decimal-dotted string to binary IP
144
*/
145
uint32_t string_to_ipv4(std::string_view str) {
12✔
146
   const auto parts = split_on(str, '.');
12✔
147

148
   if(parts.size() != 4) {
12✔
149
      throw Decoding_Error(fmt("Invalid IPv4 string '{}'", str));
×
150
   }
151

152
   uint32_t ip = 0;
153

154
   for(auto part = parts.begin(); part != parts.end(); ++part) {
60✔
155
      uint32_t octet = to_u32bit(*part);
48✔
156

157
      if(octet > 255) {
48✔
158
         throw Decoding_Error(fmt("Invalid IPv4 string '{}'", str));
×
159
      }
160

161
      ip = (ip << 8) | (octet & 0xFF);
48✔
162
   }
163

164
   return ip;
12✔
165
}
12✔
166

167
/*
168
* Convert an IP address to decimal-dotted string
169
*/
170
std::string ipv4_to_string(uint32_t ip) {
2,048✔
171
   std::string str;
2,048✔
172
   uint8_t bits[4];
2,048✔
173
   store_be(ip, bits);
2,048✔
174

175
   for(size_t i = 0; i != 4; ++i) {
10,240✔
176
      if(i > 0) {
8,192✔
177
         str += ".";
6,144✔
178
      }
179
      str += std::to_string(bits[i]);
16,384✔
180
   }
181

182
   return str;
2,048✔
183
}
×
184

185
std::string tolower_string(std::string_view in) {
694✔
186
   std::string s(in);
694✔
187
   for(size_t i = 0; i != s.size(); ++i) {
10,134✔
188
      const int cu = static_cast<unsigned char>(s[i]);
9,440✔
189
      if(std::isalpha(cu))
9,440✔
190
         s[i] = static_cast<char>(std::tolower(cu));
8,116✔
191
   }
192
   return s;
694✔
193
}
194

195
bool host_wildcard_match(std::string_view issued_, std::string_view host_) {
210✔
196
   const std::string issued = tolower_string(issued_);
210✔
197
   const std::string host = tolower_string(host_);
210✔
198

199
   if(host.empty() || issued.empty())
210✔
200
      return false;
201

202
   /*
203
   If there are embedded nulls in your issued name
204
   Well I feel bad for you son
205
   */
206
   if(std::count(issued.begin(), issued.end(), char(0)) > 0)
414✔
207
      return false;
208

209
   // If more than one wildcard, then issued name is invalid
210
   const size_t stars = std::count(issued.begin(), issued.end(), '*');
207✔
211
   if(stars > 1)
207✔
212
      return false;
213

214
   // '*' is not a valid character in DNS names so should not appear on the host side
215
   if(std::count(host.begin(), host.end(), '*') != 0)
406✔
216
      return false;
217

218
   // Similarly a DNS name can't end in .
219
   if(host[host.size() - 1] == '.')
200✔
220
      return false;
221

222
   // And a host can't have an empty name component, so reject that
223
   if(host.find("..") != std::string::npos)
200✔
224
      return false;
225

226
   // Exact match: accept
227
   if(issued == host) {
198✔
228
      return true;
229
   }
230

231
   /*
232
   Otherwise it might be a wildcard
233

234
   If the issued size is strictly longer than the hostname size it
235
   couldn't possibly be a match, even if the issued value is a
236
   wildcard. The only exception is when the wildcard ends up empty
237
   (eg www.example.com matches www*.example.com)
238
   */
239
   if(issued.size() > host.size() + 1) {
49✔
240
      return false;
241
   }
242

243
   // If no * at all then not a wildcard, and so not a match
244
   if(stars != 1) {
40✔
245
      return false;
246
   }
247

248
   /*
249
   Now walk through the issued string, making sure every character
250
   matches. When we come to the (singular) '*', jump forward in the
251
   hostname by the corresponding amount. We know exactly how much
252
   space the wildcard takes because it must be exactly `len(host) -
253
   len(issued) + 1 chars`.
254

255
   We also verify that the '*' comes in the leftmost component, and
256
   doesn't skip over any '.' in the hostname.
257
   */
258
   size_t dots_seen = 0;
259
   size_t host_idx = 0;
260

261
   for(size_t i = 0; i != issued.size(); ++i) {
291✔
262
      dots_seen += (issued[i] == '.');
275✔
263

264
      if(issued[i] == '*') {
275✔
265
         // Fail: wildcard can only come in leftmost component
266
         if(dots_seen > 0) {
30✔
267
            return false;
268
         }
269

270
         /*
271
         Since there is only one * we know the tail of the issued and
272
         hostname must be an exact match. In this case advance host_idx
273
         to match.
274
         */
275
         const size_t advance = (host.size() - issued.size() + 1);
23✔
276

277
         if(host_idx + advance > host.size())  // shouldn't happen
23✔
278
            return false;
279

280
         // Can't be any intervening .s that we would have skipped
281
         if(std::count(host.begin() + host_idx, host.begin() + host_idx + advance, '.') != 0)
46✔
282
            return false;
283

284
         host_idx += advance;
285
      } else {
286
         if(issued[i] != host[host_idx]) {
245✔
287
            return false;
288
         }
289

290
         host_idx += 1;
242✔
291
      }
292
   }
293

294
   // Wildcard issued name must have at least 3 components
295
   if(dots_seen < 2) {
16✔
296
      return false;
3✔
297
   }
298

299
   return true;
300
}
331✔
301

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