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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

36.53
/ext/yahttp/yahttp/utility.hpp
1
#pragma once
2

3
#ifndef YAHTTP_MAX_REQUEST_LINE_SIZE
4
#define YAHTTP_MAX_REQUEST_LINE_SIZE 8192
745✔
5
#endif
6

7
#ifndef YAHTTP_MAX_REQUEST_FIELDS
8
#define YAHTTP_MAX_REQUEST_FIELDS 100
36✔
9
#endif
10

11
namespace YaHTTP {
12
  static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months 
13
  static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
14

15
  bool isspace(char c);
16
  bool isspace(char c, const std::locale& loc);
17
  bool isxdigit(char c);
18
  bool isxdigit(char c, const std::locale& loc);
19
  bool isdigit(char c);
20
  bool isdigit(char c, const std::locale& loc);
21
  bool isalnum(char c);
22
  bool isalnum(char c, const std::locale& loc);
23

24
  /*! Case-Insensitive NULL safe comparator for string maps */
25
  struct ASCIICINullSafeComparator {
26
    bool operator() (const std::string& lhs, const std::string& rhs) const {
111,572✔
27
      int v;
111,572✔
28
      std::string::const_iterator lhi = lhs.begin();
111,572✔
29
      std::string::const_iterator rhi = rhs.begin();
111,572✔
30
      for(;lhi != lhs.end() && rhi != rhs.end(); lhi++, rhi++)
307,743✔
31
        if ((v = ::tolower(*lhi) - ::tolower(*rhi)) != 0) return v<0; 
297,336✔
32
      if (lhi == lhs.end() && rhi != rhs.end()) return true;
10,407✔
33
      if (lhi != lhs.end() && rhi == rhs.end()) return false;
7,358!
34
      return false; // they are equal
5,212✔
35
    }
7,358✔
36
  };
37

38
  typedef std::map<std::string,std::string,ASCIICINullSafeComparator> strstr_map_t; //<! String to String map
39

40
  /*! Represents a date/time with utc offset */
41
  class DateTime {
42
  public:
43
     bool isSet; //<! if this is initialized yet
44

45
     int year; //<! year, 0 is year 0, not 1900
46

47
     int month; //<! month, range 1-12
48
     int day; //<! day, range 1-31
49
     int wday; //<! week day, range 1-7
50

51
     int hours; //<! hours, range 0-23
52
     int minutes; //<! minutes, range 0-59
53
     int seconds; //<! seconds, range 0-60
54

55
     int utc_offset; //<! UTC offset with minutes (hhmm)
56

57
     DateTime() { 
×
58
       initialize();
×
59
     }; //<! Construct and initialize
×
60

61
     void initialize() {
×
62
       isSet = false; 
×
63
       year = month = day = wday = hours = minutes = seconds = utc_offset = 0;
×
64
       month = 1; // it's invalid otherwise
×
65
     }; //<! Creates year 0 date
×
66

67
     void setLocal() {
×
68
       fromLocaltime(time((time_t*)NULL)); 
×
69
     }; //<! sets current local time
×
70

71
     void setGm() {
×
72
       fromGmtime(time((time_t*)NULL));
×
73
     }; //<! sets current gmtime (almost UTC)
×
74

75
     void fromLocaltime(time_t t) {
×
76
#ifdef HAVE_LOCALTIME_R
×
77
       struct tm tm;
×
78
       localtime_r(&t, &tm);
×
79
       fromTm(&tm);
×
80
#else
×
81
       struct tm *tm;
×
82
       #error define HAVE_LOCALTIME_R
×
83
       tm = localtime(&t); // lgtm [cpp/potentially-dangerous-function]
×
84
       fromTm(tm);
×
85
#endif
×
86
#ifndef HAVE_TM_GMTOFF
×
87
       time_t t2;
×
88
# ifdef HAVE_LOCALTIME_R
×
89
       gmtime_r(&t, &tm);
×
90
       t2 = mktime(&tm);
×
91
# else
×
92
       #error define HAVE_LOCALTIME_R
×
93
       tm = gmtime(&t); // lgtm [cpp/potentially-dangerous-function]
×
94
       t2 = mktime(tm);
×
95
# endif
×
96
       this->utc_offset = ((t2-t)/10)*10; // removes any possible differences. 
×
97
#endif
×
98
     }; //<! uses localtime for time
×
99

100
     void fromGmtime(time_t t) {
×
101
#ifdef HAVE_GMTIME_R
×
102
       struct tm tm;
×
103
       gmtime_r(&t, &tm);
×
104
       fromTm(&tm);
×
105
#else
×
106
       struct tm *tm;
×
107
       #error define HAVE_GMTIME_R
×
108
       tm = gmtime(&t);// lgtm [cpp/potentially-dangerous-function]
×
109
       fromTm(tm);
×
110
#endif
×
111
#ifndef HAVE_TM_GMTOFF
×
112
       this->utc_offset = 0;
×
113
#endif
×
114
     }; //<! uses gmtime for time
×
115

116
     void fromTm(const struct tm *tm) {
×
117
       year = tm->tm_year + 1900;
×
118
       month = tm->tm_mon + 1;
×
119
       day = tm->tm_mday;
×
120
       hours = tm->tm_hour;
×
121
       minutes = tm->tm_min;
×
122
       seconds = tm->tm_sec;
×
123
       wday = tm->tm_wday;
×
124
#ifdef HAVE_TM_GMTOFF
125
       utc_offset = tm->tm_gmtoff;
126
#endif
127
       isSet = true;
×
128
     }; //<! parses date from struct tm 
×
129

130
     void validate() const {
×
131
       if (wday < 0 || wday > 6) throw std::range_error("Invalid date");
×
132
       if (month < 1 || month > 12) throw std::range_error("Invalid date");
×
133
       if (year < 0) throw std::range_error("Invalid date");
×
134
       if (hours < 0 || hours > 23 ||
×
135
           minutes < 0 || minutes > 59 ||
×
136
           seconds < 0 || seconds > 60) throw std::range_error("Invalid date");
×
137
     }; //<! make sure we are within ranges (not a *REAL* validation, just range check)
×
138

139
     std::string rfc_str() const {
×
140
       std::ostringstream oss;
×
141
       validate();
×
142
       oss << DAYS[wday] << ", " << std::setfill('0') << std::setw(2) << day << " " << MONTHS[month] << " " <<
×
143
          std::setfill('0') << std::setw(2) <<  year << " " << 
×
144
          std::setfill('0') << std::setw(2) << hours << ":" << 
×
145
          std::setfill('0') << std::setw(2) << minutes << ":" << 
×
146
          std::setfill('0') << std::setw(2) << seconds << " ";
×
147
       if (utc_offset>=0) oss << "+";
×
148
       else oss << "-";
×
149
       int tmp_off = ( utc_offset < 0 ? utc_offset*-1 : utc_offset ); 
×
150
       oss << std::setfill('0') << std::setw(2) << (tmp_off/3600);
×
151
       oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60;
×
152

×
153
       return oss.str(); 
×
154
     }; //<! converts this date into a RFC-822 format
×
155
 
156
     std::string cookie_str() const {
×
157
       std::ostringstream oss;
×
158
       validate();
×
159
       oss << std::setfill('0') << std::setw(2) << day << "-" << MONTHS[month] << "-" << year << " " <<
×
160
         std::setfill('0') << std::setw(2) << hours << ":" << 
×
161
         std::setfill('0') << std::setw(2) << minutes << ":" << 
×
162
         std::setfill('0') << std::setw(2) << seconds << " GMT";
×
163
       return oss.str();
×
164
     }; //<! converts this date into a HTTP Cookie date
×
165
 
166
     void parse822(const std::string &rfc822_date) {
×
167
       struct tm tm;
×
168
       const char *ptr;
×
169
#ifdef HAVE_TM_GMTOFF
×
170
       if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) {
×
171
#else
×
172
        if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T", &tm)) != NULL) {
×
173
          int sign;
×
174
            // parse the timezone parameter
×
175
          while(*ptr && YaHTTP::isspace(*ptr)) ptr++;
×
176
          if (*ptr == '+') sign = 0;
×
177
          else if (*ptr == '-') sign = -1;
×
178
          else throw YaHTTP::ParseError("Unparseable date");
×
179
          ptr++;
×
180
          utc_offset = ::atoi(ptr) * sign;
×
181
          while(*ptr != '\0' && YaHTTP::isdigit(*ptr)) ptr++;
×
182
#endif
×
183
          while(*ptr != '\0' && YaHTTP::isspace(*ptr)) ptr++;
×
184
          if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date"); // must be final.
×
185
          fromTm(&tm);
×
186
       } else {
×
187
          throw YaHTTP::ParseError("Unparseable date");
×
188
       }
×
189
     }; //<! parses RFC-822 date
×
190

191
     void parseCookie(const std::string &cookie_date) {
×
192
       struct tm tm;
×
193
       const char *ptr;
×
194
       if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL
×
195
#ifdef HAVE_TM_GMTOFF
196
          || (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T %z", &tm)) != NULL
197
          || (ptr = strptime(cookie_date.c_str(), "%a, %d-%b-%Y %T %Z", &tm)) != NULL
198
#endif
199
          ) {
×
200
          while(*ptr != '\0' && ( YaHTTP::isspace(*ptr) || YaHTTP::isalnum(*ptr) )) ptr++;
×
201
          if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date (non-final)"); // must be final.
×
202
          fromTm(&tm);
×
203
          this->utc_offset = 0;
×
204
       } else {
×
205
          std::cout << cookie_date << std::endl;
×
206
          throw YaHTTP::ParseError("Unparseable date (did not match pattern cookie)");
×
207
       }
×
208
     }; //<! parses HTTP Cookie date
×
209

210
     time_t unixtime() const {
×
211
       struct tm tm;
×
212
       tm.tm_year = year-1900;
×
213
       tm.tm_mon = month-1;
×
214
       tm.tm_mday = day;
×
215
       tm.tm_hour = hours;
×
216
       tm.tm_min = minutes;
×
217
       tm.tm_sec = seconds;
×
218
       tm.tm_isdst = 0;
×
219
#ifdef HAVE_TM_GMTOFF
×
220
       tm.tm_gmtoff = utc_offset;
×
221
#endif
×
222
       return mktime(&tm);
×
223
     }; //<! returns this datetime as unixtime. will not work for dates before 1970/1/1 00:00:00 GMT
×
224
  };
225

226
  /*! Various helpers needed in the code */ 
227
  class Utility {
228
  public:
229
    static std::string decodeURL(const std::string& component) {
356✔
230
        std::string result = component;
356✔
231
        size_t pos1,pos2;
356✔
232
        pos2 = 0;
356✔
233
        while((pos1 = result.find_first_of("%", pos2))!=std::string::npos) {
356!
234
           std::string code;
×
235
           char a,b,c;
×
236
           if (pos1 + 2 > result.length()) return result; // end of result
×
237
           code = result.substr(pos1+1, 2);
×
238
           a = std::tolower(code[0]); b = std::tolower(code[1]);
×
239

240
           if ((( '0' > a || a > '9') && ('a' > a || a > 'f')) ||
×
241
              (( '0' > b || b > '9') && ('a' > b || b > 'f'))) {
×
242
              pos2 = pos1+3;
×
243
              continue;
×
244
           }
×
245

246
           if ('0' <= a && a <= '9') a = a - '0';
×
247
           else if ('a' <= a && a <= 'f') a = a - 'a' + 0x0a;
×
248
           if ('0' <= b && b <= '9') b = b - '0';
×
249
           else if ('a' <= b && b <= 'f') b = b - 'a' + 0x0a;
×
250

251
           c = (a<<4)+b;
×
252
           result = result.replace(pos1,3,1,c);
×
253
           pos2=pos1;
×
254
        }
×
255
        return result;
356✔
256
    }; //<! Decodes %xx from string into bytes
356✔
257
    
258
    static std::string encodeURL(const std::string& component, bool asUrl = true) {
247✔
259
      std::string result = component;
247✔
260
      std::string skip = "+-.:,&;_#%[]?/@(){}=";
247✔
261
      char repl[3];
247✔
262
      size_t pos;
247✔
263
      for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) {
6,970✔
264
        if (!YaHTTP::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
6,723!
265
          // replace with different thing
266
          pos = std::distance(result.begin(), iter);
1,575✔
267
          ::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter));
1,575✔
268
          result = result.replace(pos, 1, "%", 1).insert(pos+1, repl, 2);
1,575✔
269
          iter = result.begin() + pos + 2;
1,575✔
270
        }
1,575✔
271
      }
6,723✔
272
      return result;
247✔
273
    }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url
247✔
274

275
    static std::string encodeURL(const std::wstring& component, bool asUrl = true) {
×
276
      unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]);
×
277
      std::size_t s = component.size() * sizeof((*component.begin()));
×
278
      std::vector<unsigned char> vec(p, p+s);
×
279

×
280
      std::ostringstream result;
×
281
      std::string skip = "+-.,&;_#%[]?/@(){}=";
×
282
      for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
×
283
        if (!YaHTTP::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
×
284
          // bit more complex replace
×
285
          result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter);
×
286
        } else result << (char)*iter;
×
287
      }
×
288
      return result.str();
×
289
    }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url, for wide strings, returns ordinary string
×
290

291
    static std::string status2text(int status) {
504✔
292
       switch(status) {
504✔
293
       case 200:
394✔
294
           return "OK";
394✔
295
       case 201:
9✔
296
           return "Created";
9✔
297
       case 202:
×
298
           return "Accepted";
×
299
       case 203:
×
300
           return "Non-Authoritative Information";
×
301
       case 204:
2✔
302
           return "No Content";
2✔
303
       case 205:
×
304
           return "Reset Content";
×
305
       case 206:
×
306
           return "Partial Content";
×
307
       case 300:
×
308
           return "Multiple Choices";
×
309
       case 301:
×
310
           return "Moved Permanently";
×
311
       case 302:
×
312
           return "Found";
×
313
       case 303:
×
314
           return "See Other";
×
315
       case 304:
×
316
           return "Not Modified";
×
317
       case 305:
×
318
           return "Use Proxy";
×
319
       case 307:
×
320
           return "Temporary Redirect";
×
321
       case 400:
48✔
322
           return "Bad Request";
48✔
323
       case 401:
40✔
324
           return "Unauthorized";
40✔
325
       case 402:
×
326
           return "Payment Required";
×
327
       case 403: 
×
328
           return "Forbidden";
×
329
       case 404:
3✔
330
           return "Not Found";
3✔
331
       case 405:
2✔
332
           return "Method Not Allowed";
2✔
333
       case 406:
×
334
           return "Not Acceptable";
×
335
       case 407:
×
336
           return "Proxy Authentication Required";
×
337
       case 408:
×
338
           return "Request Time-out";
×
339
       case 409:
×
340
           return "Conflict";
×
341
       case 410:
×
342
           return "Gone";
×
343
       case 411:
×
344
           return "Length Required";
×
345
       case 412:
×
346
           return "Precondition Failed";
×
347
       case 413:
×
348
           return "Request Entity Too Large";
×
349
       case 414:
×
350
           return "Request-URI Too Large";
×
351
       case 415:
×
352
           return "Unsupported Media Type";
×
353
       case 416:
×
354
           return "Requested range not satisfiable";
×
355
       case 417:
×
356
           return "Expectation Failed";
×
357
       case 422:
6✔
358
           return "Unprocessable Entity";
6✔
359
       case 500:
×
360
           return "Internal Server Error";
×
361
       case 501:
×
362
           return "Not Implemented";
×
363
       case 502:
×
364
           return "Bad Gateway";
×
365
       case 503:
×
366
           return "Service Unavailable";
×
367
       case 504:
×
368
           return "Gateway Time-out";
×
369
       case 505:
×
370
           return "HTTP Version not supported";
×
371
       default:
×
372
           return "Unknown Status";
×
373
       }
504✔
374
    }; //<! static HTTP codes to text mappings
504✔
375

376
    static strstr_map_t parseUrlParameters(const std::string& parameters) {
745✔
377
      if (parameters.size() > YAHTTP_MAX_REQUEST_LINE_SIZE) {
745!
378
        return {};
×
379
      }
×
380
      std::string::size_type pos = 0;
745✔
381
      strstr_map_t parameter_map;
745✔
382
      while (pos != std::string::npos) {
781!
383
        // find next parameter start
384
        std::string::size_type nextpos = parameters.find("&", pos);
781✔
385
        std::string::size_type delim = parameters.find("=", pos);
781✔
386
        if (delim > nextpos) {
781!
387
          delim = nextpos;
×
388
        }
×
389
        std::string key;
781✔
390
        std::string value;
781✔
391
        if (delim == std::string::npos) {
781✔
392
          key = parameters.substr(pos);
606✔
393
        } else {
606✔
394
          key = parameters.substr(pos, delim-pos);
175✔
395
          if (nextpos == std::string::npos) {
175✔
396
            value = parameters.substr(delim+1);
139✔
397
          } else {
139✔
398
            value = parameters.substr(delim+1, nextpos-delim-1);
36✔
399
          }
36✔
400
        }
175✔
401
        if (key.empty()) {
781✔
402
          // no parameters at all
403
          break;
606✔
404
        }
606✔
405
        parameter_map[decodeURL(key)] = decodeURL(value);
175✔
406
        if (nextpos == std::string::npos) {
175✔
407
          // no more parameters left
408
          break;
139✔
409
        }
139✔
410
        if (parameter_map.size() >= YAHTTP_MAX_REQUEST_FIELDS) {
36!
411
          break;
×
412
        }
×
413

414
        pos = nextpos+1;
36✔
415
      }
36✔
416
      return parameter_map;
745✔
417
    }; //<! parses URL parameters into string map 
745✔
418

419
    static bool iequals(const std::string& a, const std::string& b, size_t length) {
147✔
420
      std::string::const_iterator ai, bi;
147✔
421
      size_t i;
147✔
422
      for(ai = a.begin(), bi = b.begin(), i = 0; ai != a.end() && bi != b.end() && i < length; ai++,bi++,i++) {
351!
423
        if (::toupper(*ai) != ::toupper(*bi)) return false;
351✔
424
      }
351✔
425

426
      if (ai == a.end() && bi == b.end()) return true;
×
427
      if ((ai == a.end() && bi != b.end()) ||
×
428
          (ai != a.end() && bi == b.end())) return false;
×
429
      
430
      return ::toupper(*ai) == ::toupper(*bi);
×
431
    }; //<! case-insensitive comparison with length
×
432

433
    static bool iequals(const std::string& a, const std::string& b) {
×
434
      if (a.size() != b.size()) return false;
×
435
      return iequals(a,b,a.size());
×
436
    }; //<! case-insensitive comparison
×
437

438
    static void trimLeft(std::string &str) {
3,181✔
439
       const std::locale &loc = std::locale::classic();
3,181✔
440
       std::string::iterator iter = str.begin();
3,181✔
441
       while(iter != str.end() && YaHTTP::isspace(*iter, loc)) iter++;
6,362!
442
       str.erase(str.begin(), iter);
3,181✔
443
    }; //<! removes whitespace from left
3,181✔
444

445
    static void trimRight(std::string &str) {
3,181✔
446
       const std::locale &loc = std::locale::classic();
3,181✔
447
       std::string::reverse_iterator iter = str.rbegin();
3,181✔
448
       while(iter != str.rend() && YaHTTP::isspace(*iter, loc)) iter++;
3,181!
449
       str.erase(iter.base(), str.end());
3,181✔
450
    }; //<! removes whitespace from right
3,181✔
451

452
    static void trim(std::string &str) {
3,181✔
453
       trimLeft(str);
3,181✔
454
       trimRight(str);
3,181✔
455
    }; //<! removes whitespace from left and right
3,181✔
456

457
    static std::string camelizeHeader(const std::string &str) {
8,536✔
458
       std::string::const_iterator iter = str.begin();
8,536✔
459
       std::string result;
8,536✔
460
       const std::locale &loc = std::locale::classic();
8,536✔
461

462
       bool doNext = true;
8,536✔
463

464
       while(iter != str.end()) {
154,356✔
465
         if (doNext) 
145,820✔
466
            result.insert(result.end(), std::toupper(*iter, loc));
23,126✔
467
         else 
122,694✔
468
            result.insert(result.end(), std::tolower(*iter, loc)); 
122,694✔
469
         doNext = (*(iter++) == '-');
145,820✔
470
       }
145,820✔
471

472
       return result;
8,536✔
473
   }; //<! camelizes headers, such as, content-type => Content-Type
8,536✔
474
  };
475
};
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

© 2025 Coveralls, Inc