nghttp2/src/util.cc

1398 lines
33 KiB
C++
Raw Normal View History

2012-01-29 16:34:10 +01:00
/*
2014-03-30 12:09:21 +02:00
* nghttp2 - HTTP/2 C Library
2012-01-29 16:34:10 +01:00
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "util.h"
2015-05-13 15:30:35 +02:00
#ifdef HAVE_TIME_H
#include <time.h>
2015-05-13 15:30:35 +02:00
#endif // HAVE_TIME_H
#include <sys/types.h>
2015-05-13 15:30:35 +02:00
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
2015-05-13 15:30:35 +02:00
#endif // HAVE_SYS_SOCKET_H
#ifdef HAVE_NETDB_H
#include <netdb.h>
2015-05-13 15:30:35 +02:00
#endif // HAVE_NETDB_H
#include <sys/stat.h>
2015-05-13 15:30:35 +02:00
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
2015-05-13 15:30:35 +02:00
#endif // HAVE_FCNTL_H
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
2015-05-13 15:30:35 +02:00
#endif // HAVE_NETINET_IN_H
#include <netinet/tcp.h>
2015-05-13 15:30:35 +02:00
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
2015-05-13 15:30:35 +02:00
#endif // HAVE_ARPA_INET_H
#include <cmath>
#include <cerrno>
#include <cassert>
2012-01-29 16:34:10 +01:00
#include <cstdio>
#include <cstring>
2014-01-08 17:27:56 +01:00
#include <iostream>
#include <fstream>
2012-01-29 16:34:10 +01:00
#include <openssl/evp.h>
2014-11-14 15:14:39 +01:00
#include <nghttp2/nghttp2.h>
#include "ssl_compat.h"
#include "timegm.h"
2013-07-12 17:19:03 +02:00
namespace nghttp2 {
2012-01-29 16:34:10 +01:00
namespace util {
2014-05-14 15:39:28 +02:00
const char UPPER_XDIGITS[] = "0123456789ABCDEF";
bool in_rfc3986_unreserved_chars(const char c) {
2016-02-07 10:12:57 +01:00
static constexpr char unreserved[] = {'-', '.', '_', '~'};
return is_alpha(c) || is_digit(c) ||
std::find(std::begin(unreserved), std::end(unreserved), c) !=
std::end(unreserved);
2012-01-29 16:34:10 +01:00
}
bool in_rfc3986_sub_delims(const char c) {
2016-02-07 10:12:57 +01:00
static constexpr char sub_delims[] = {'!', '$', '&', '\'', '(', ')',
'*', '+', ',', ';', '='};
return std::find(std::begin(sub_delims), std::end(sub_delims), c) !=
std::end(sub_delims);
}
std::string percent_encode(const unsigned char *target, size_t len) {
2012-01-29 16:34:10 +01:00
std::string dest;
2014-11-27 15:39:04 +01:00
for (size_t i = 0; i < len; ++i) {
2014-05-14 15:39:28 +02:00
unsigned char c = target[i];
if (in_rfc3986_unreserved_chars(c)) {
2014-05-14 15:39:28 +02:00
dest += c;
2012-01-29 16:34:10 +01:00
} else {
dest += '%';
2014-05-14 15:39:28 +02:00
dest += UPPER_XDIGITS[c >> 4];
dest += UPPER_XDIGITS[(c & 0x0f)];
2012-01-29 16:34:10 +01:00
}
}
return dest;
}
std::string percent_encode(const std::string &target) {
return percent_encode(reinterpret_cast<const unsigned char *>(target.c_str()),
target.size());
2012-01-29 16:34:10 +01:00
}
std::string percent_encode_path(const std::string &s) {
std::string dest;
for (auto c : s) {
if (in_rfc3986_unreserved_chars(c) || in_rfc3986_sub_delims(c) ||
c == '/') {
dest += c;
continue;
}
dest += '%';
dest += UPPER_XDIGITS[(c >> 4) & 0x0f];
dest += UPPER_XDIGITS[(c & 0x0f)];
}
return dest;
}
2014-11-27 15:39:04 +01:00
bool in_token(char c) {
2016-02-07 10:12:57 +01:00
static constexpr char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
'-', '.', '^', '_', '`', '|', '~'};
return is_alpha(c) || is_digit(c) ||
std::find(std::begin(extra), std::end(extra), c) != std::end(extra);
}
bool in_attr_char(char c) {
2016-02-07 10:12:57 +01:00
static constexpr char bad[] = {'*', '\'', '%'};
return util::in_token(c) &&
std::find(std::begin(bad), std::end(bad), c) == std::end(bad);
}
StringRef percent_encode_token(BlockAllocator &balloc,
const StringRef &target) {
auto iov = make_byte_ref(balloc, target.size() * 3 + 1);
auto p = iov.base;
2016-01-26 07:23:42 +01:00
for (auto first = std::begin(target); first != std::end(target); ++first) {
uint8_t c = *first;
2014-05-14 15:39:28 +02:00
2014-11-27 15:39:04 +01:00
if (c != '%' && in_token(c)) {
2016-01-26 07:23:42 +01:00
*p++ = c;
continue;
}
2016-01-26 07:23:42 +01:00
*p++ = '%';
*p++ = UPPER_XDIGITS[c >> 4];
*p++ = UPPER_XDIGITS[(c & 0x0f)];
}
*p = '\0';
return StringRef{iov.base, p};
}
2015-02-10 14:35:10 +01:00
uint32_t hex_to_uint(char c) {
if (c <= '9') {
return c - '0';
}
if (c <= 'Z') {
return c - 'A' + 10;
}
if (c <= 'z') {
return c - 'a' + 10;
}
return c;
}
2014-11-27 15:39:04 +01:00
std::string quote_string(const std::string &target) {
auto cnt = std::count(std::begin(target), std::end(target), '"');
2014-11-27 15:39:04 +01:00
if (cnt == 0) {
return target;
}
std::string res;
res.reserve(target.size() + cnt);
2014-11-27 15:39:04 +01:00
for (auto c : target) {
if (c == '"') {
res += "\\\"";
} else {
res += c;
}
}
return res;
}
2014-11-10 13:26:48 +01:00
namespace {
2014-11-27 15:39:04 +01:00
template <typename Iterator>
Iterator cpydig(Iterator d, uint32_t n, size_t len) {
2014-11-10 13:26:48 +01:00
auto p = d + len - 1;
do {
*p-- = (n % 10) + '0';
n /= 10;
2014-11-27 15:39:04 +01:00
} while (p >= d);
2014-11-10 13:26:48 +01:00
return d + len;
}
} // namespace
namespace {
2014-11-27 15:39:04 +01:00
const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
2014-11-10 13:26:48 +01:00
} // namespace
2014-11-27 15:39:04 +01:00
std::string http_date(time_t t) {
2014-11-10 13:26:48 +01:00
struct tm tms;
std::string res;
2014-05-14 14:23:21 +02:00
2014-11-27 15:39:04 +01:00
if (gmtime_r(&t, &tms) == nullptr) {
2014-11-10 13:26:48 +01:00
return res;
2014-05-14 14:23:21 +02:00
}
2014-11-10 13:26:48 +01:00
/* Sat, 27 Sep 2014 06:31:15 GMT */
res.resize(29);
auto p = std::begin(res);
auto s = DAY_OF_WEEK[tms.tm_wday];
2015-02-06 13:35:03 +01:00
p = std::copy_n(s, 3, p);
2014-11-10 13:26:48 +01:00
*p++ = ',';
*p++ = ' ';
p = cpydig(p, tms.tm_mday, 2);
*p++ = ' ';
s = MONTH[tms.tm_mon];
2015-02-06 13:35:03 +01:00
p = std::copy_n(s, 3, p);
2014-11-10 13:26:48 +01:00
*p++ = ' ';
p = cpydig(p, tms.tm_year + 1900, 4);
*p++ = ' ';
p = cpydig(p, tms.tm_hour, 2);
*p++ = ':';
p = cpydig(p, tms.tm_min, 2);
*p++ = ':';
p = cpydig(p, tms.tm_sec, 2);
s = " GMT";
2015-02-06 13:35:03 +01:00
p = std::copy_n(s, 4, p);
2014-11-10 13:26:48 +01:00
return res;
}
2014-11-27 15:39:04 +01:00
std::string common_log_date(time_t t) {
struct tm tms;
2014-11-27 15:39:04 +01:00
if (localtime_r(&t, &tms) == nullptr) {
return "";
}
// Format data like this:
// 03/Jul/2014:00:19:38 +0900
std::string res;
res.resize(26);
auto p = std::begin(res);
p = cpydig(p, tms.tm_mday, 2);
*p++ = '/';
auto s = MONTH[tms.tm_mon];
2015-02-06 13:35:03 +01:00
p = std::copy_n(s, 3, p);
*p++ = '/';
p = cpydig(p, tms.tm_year + 1900, 4);
*p++ = ':';
p = cpydig(p, tms.tm_hour, 2);
*p++ = ':';
p = cpydig(p, tms.tm_min, 2);
*p++ = ':';
p = cpydig(p, tms.tm_sec, 2);
*p++ = ' ';
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
auto gmtoff = tms.tm_gmtoff;
#else // !HAVE_STRUCT_TM_TM_GMTOFF
2015-07-15 16:44:24 +02:00
auto gmtoff = nghttp2_timegm(&tms) - t;
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
2014-11-27 15:39:04 +01:00
if (gmtoff >= 0) {
*p++ = '+';
} else {
*p++ = '-';
gmtoff = -gmtoff;
}
p = cpydig(p, gmtoff / 3600, 2);
p = cpydig(p, (gmtoff % 3600) / 60, 2);
return res;
}
2014-11-27 15:39:04 +01:00
std::string iso8601_date(int64_t ms) {
time_t sec = ms / 1000;
tm tms;
2014-11-27 15:39:04 +01:00
if (localtime_r(&sec, &tms) == nullptr) {
return "";
}
// Format data like this:
// 2014-11-15T12:58:24.741Z
// 2014-11-15T12:58:24.741+09:00
std::string res;
res.resize(29);
auto p = std::begin(res);
p = cpydig(p, tms.tm_year + 1900, 4);
*p++ = '-';
p = cpydig(p, tms.tm_mon + 1, 2);
*p++ = '-';
p = cpydig(p, tms.tm_mday, 2);
*p++ = 'T';
p = cpydig(p, tms.tm_hour, 2);
*p++ = ':';
p = cpydig(p, tms.tm_min, 2);
*p++ = ':';
p = cpydig(p, tms.tm_sec, 2);
*p++ = '.';
p = cpydig(p, ms % 1000, 3);
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
auto gmtoff = tms.tm_gmtoff;
#else // !HAVE_STRUCT_TM_TM_GMTOFF
2015-07-15 16:44:24 +02:00
auto gmtoff = nghttp2_timegm(&tms) - sec;
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
2014-11-27 15:39:04 +01:00
if (gmtoff == 0) {
*p++ = 'Z';
} else {
2014-11-27 15:39:04 +01:00
if (gmtoff > 0) {
*p++ = '+';
} else {
*p++ = '-';
gmtoff = -gmtoff;
}
p = cpydig(p, gmtoff / 3600, 2);
*p++ = ':';
p = cpydig(p, (gmtoff % 3600) / 60, 2);
}
res.resize(p - std::begin(res));
return res;
}
2016-03-12 09:31:48 +01:00
time_t parse_http_date(const StringRef &s) {
tm tm{};
2014-11-27 15:39:04 +01:00
char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm);
if (r == 0) {
return 0;
}
return nghttp2_timegm_without_yday(&tm);
}
2014-11-27 15:39:04 +01:00
char upcase(char c) {
if ('a' <= c && c <= 'z') {
return c - 'a' + 'A';
} else {
return c;
}
}
2014-05-14 16:09:33 +02:00
namespace {
const char LOWER_XDIGITS[] = "0123456789abcdef";
} // namespace
2014-11-27 15:39:04 +01:00
std::string format_hex(const unsigned char *s, size_t len) {
2013-07-15 17:15:04 +02:00
std::string res;
2014-05-14 16:09:33 +02:00
res.resize(len * 2);
2014-11-27 15:39:04 +01:00
for (size_t i = 0; i < len; ++i) {
2014-05-14 16:09:33 +02:00
unsigned char c = s[i];
res[i * 2] = LOWER_XDIGITS[c >> 4];
res[i * 2 + 1] = LOWER_XDIGITS[c & 0x0f];
2013-07-15 17:15:04 +02:00
}
return res;
}
2014-11-27 15:39:04 +01:00
void to_token68(std::string &base64str) {
2015-02-10 14:35:10 +01:00
std::transform(std::begin(base64str), std::end(base64str),
std::begin(base64str), [](char c) {
2015-11-12 16:53:29 +01:00
switch (c) {
case '+':
return '-';
case '/':
return '_';
default:
return c;
}
});
2015-02-10 17:05:22 +01:00
base64str.erase(std::find(std::begin(base64str), std::end(base64str), '='),
std::end(base64str));
}
2014-11-27 15:39:04 +01:00
void to_base64(std::string &token68str) {
2015-02-10 14:35:10 +01:00
std::transform(std::begin(token68str), std::end(token68str),
std::begin(token68str), [](char c) {
2015-11-12 16:53:29 +01:00
switch (c) {
case '-':
return '+';
case '_':
return '/';
default:
return c;
}
});
2014-11-27 15:39:04 +01:00
if (token68str.size() & 0x3) {
2013-11-09 08:18:01 +01:00
token68str.append(4 - (token68str.size() & 0x3), '=');
}
return;
}
2014-01-08 17:27:56 +01:00
namespace {
// Calculates DamerauLevenshtein distance between c-string a and b
// with given costs. swapcost, subcost, addcost and delcost are cost
// to swap 2 adjacent characters, substitute characters, add character
// and delete character respectively.
int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost,
int subcost, int addcost, int delcost) {
2014-11-27 15:39:04 +01:00
auto dp = std::vector<std::vector<int>>(3, std::vector<int>(blen + 1));
for (int i = 0; i <= blen; ++i) {
2014-01-08 17:27:56 +01:00
dp[1][i] = i;
}
2014-11-27 15:39:04 +01:00
for (int i = 1; i <= alen; ++i) {
2014-01-08 17:27:56 +01:00
dp[0][0] = i;
2014-11-27 15:39:04 +01:00
for (int j = 1; j <= blen; ++j) {
dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost);
if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] &&
a[i - 1] == b[j - 2]) {
dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost);
2014-01-08 17:27:56 +01:00
}
dp[0][j] = std::min(dp[0][j],
2014-11-27 15:39:04 +01:00
std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost));
2014-01-08 17:27:56 +01:00
}
2014-11-27 15:39:04 +01:00
std::rotate(std::begin(dp), std::begin(dp) + 2, std::end(dp));
2014-01-08 17:27:56 +01:00
}
return dp[1][blen];
}
} // namespace
2014-11-27 15:39:04 +01:00
void show_candidates(const char *unkopt, option *options) {
for (; *unkopt == '-'; ++unkopt)
;
if (*unkopt == '\0') {
2014-01-08 17:27:56 +01:00
return;
}
auto unkoptend = unkopt;
for (; *unkoptend && *unkoptend != '='; ++unkoptend)
;
auto unkoptlen = unkoptend - unkopt;
if (unkoptlen == 0) {
return;
}
int prefix_match = 0;
2014-11-27 15:39:04 +01:00
auto cands = std::vector<std::pair<int, const char *>>();
for (size_t i = 0; options[i].name != nullptr; ++i) {
auto optnamelen = strlen(options[i].name);
// Use cost 0 for prefix match
2015-11-27 16:31:42 +01:00
if (istarts_with(options[i].name, options[i].name + optnamelen, unkopt,
unkopt + unkoptlen)) {
2015-02-02 14:40:41 +01:00
if (optnamelen == static_cast<size_t>(unkoptlen)) {
// Exact match, then we don't show any condidates.
2014-11-27 15:39:04 +01:00
return;
}
++prefix_match;
cands.emplace_back(0, options[i].name);
continue;
}
// Use cost 0 for suffix match, but match at least 3 characters
2014-11-27 15:39:04 +01:00
if (unkoptlen >= 3 &&
2015-11-27 16:36:43 +01:00
iends_with(options[i].name, options[i].name + optnamelen, unkopt,
unkopt + unkoptlen)) {
2014-01-08 17:27:56 +01:00
cands.emplace_back(0, options[i].name);
continue;
}
// cost values are borrowed from git, help.c.
int sim =
levenshtein(unkopt, unkoptlen, options[i].name, optnamelen, 0, 2, 1, 3);
2014-01-08 17:27:56 +01:00
cands.emplace_back(sim, options[i].name);
}
2014-11-27 15:39:04 +01:00
if (prefix_match == 1 || cands.empty()) {
2014-01-08 17:27:56 +01:00
return;
}
std::sort(std::begin(cands), std::end(cands));
int threshold = cands[0].first;
// threshold value is a magic value.
2014-11-27 15:39:04 +01:00
if (threshold > 6) {
2014-01-08 17:27:56 +01:00
return;
}
std::cerr << "\nDid you mean:\n";
2014-11-27 15:39:04 +01:00
for (auto &item : cands) {
if (item.first > threshold) {
2014-01-08 17:27:56 +01:00
break;
}
std::cerr << "\t--" << item.second << "\n";
}
}
2014-11-27 15:39:04 +01:00
bool has_uri_field(const http_parser_url &u, http_parser_url_fields field) {
return u.field_set & (1 << field);
}
2014-11-27 15:39:04 +01:00
bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2,
const http_parser_url &u2, http_parser_url_fields field) {
if (!has_uri_field(u1, field)) {
if (!has_uri_field(u2, field)) {
return true;
} else {
return false;
}
2014-11-27 15:39:04 +01:00
} else if (!has_uri_field(u2, field)) {
return false;
}
2014-11-27 15:39:04 +01:00
if (u1.field_data[field].len != u2.field_data[field].len) {
return false;
}
2014-11-27 15:39:04 +01:00
return memcmp(uri1 + u1.field_data[field].off,
uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0;
}
bool fieldeq(const char *uri, const http_parser_url &u,
2014-11-27 15:39:04 +01:00
http_parser_url_fields field, const char *t) {
if (!has_uri_field(u, field)) {
if (!t[0]) {
return true;
} else {
return false;
}
2014-11-27 15:39:04 +01:00
} else if (!t[0]) {
return false;
}
int i, len = u.field_data[field].len;
2014-11-27 15:39:04 +01:00
const char *p = uri + u.field_data[field].off;
for (i = 0; i < len && t[i] && p[i] == t[i]; ++i)
;
return i == len && !t[i];
}
StringRef get_uri_field(const char *uri, const http_parser_url &u,
http_parser_url_fields field) {
if (!util::has_uri_field(u, field)) {
return StringRef{};
}
return StringRef{uri + u.field_data[field].off, u.field_data[field].len};
}
2014-11-27 15:39:04 +01:00
uint16_t get_default_port(const char *uri, const http_parser_url &u) {
if (util::fieldeq(uri, u, UF_SCHEMA, "https")) {
return 443;
2014-11-27 15:39:04 +01:00
} else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) {
return 80;
} else {
return 443;
}
}
2014-11-27 15:39:04 +01:00
bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2,
const http_parser_url &u2) {
uint16_t port1, port2;
2014-11-27 15:39:04 +01:00
port1 =
util::has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1);
port2 =
util::has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2);
return port1 == port2;
}
2014-11-27 15:39:04 +01:00
void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
http_parser_url_fields field) {
if (util::has_uri_field(u, field)) {
o.write(uri + u.field_data[field].off, u.field_data[field].len);
}
}
2014-11-27 15:39:04 +01:00
bool numeric_host(const char *hostname) {
return numeric_host(hostname, AF_INET) || numeric_host(hostname, AF_INET6);
}
bool numeric_host(const char *hostname, int family) {
int rv;
std::array<uint8_t, sizeof(struct in6_addr)> dst;
rv = inet_pton(family, hostname, dst.data());
return rv == 1;
}
std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
std::array<char, NI_MAXHOST> host;
auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0,
NI_NUMERICHOST);
if (rv != 0) {
return "unknown";
}
return host.data();
}
std::string to_numeric_addr(const Address *addr) {
auto family = addr->su.storage.ss_family;
if (family == AF_UNIX) {
return addr->su.un.sun_path;
}
std::array<char, NI_MAXHOST> host;
std::array<char, NI_MAXSERV> serv;
auto rv =
getnameinfo(&addr->su.sa, addr->len, host.data(), host.size(),
serv.data(), serv.size(), NI_NUMERICHOST | NI_NUMERICSERV);
if (rv != 0) {
return "unknown";
}
auto hostlen = strlen(host.data());
auto servlen = strlen(serv.data());
std::string s;
char *p;
if (family == AF_INET6) {
s.resize(hostlen + servlen + 2 + 1);
p = &s[0];
*p++ = '[';
p = std::copy_n(host.data(), hostlen, p);
*p++ = ']';
} else {
s.resize(hostlen + servlen + 1);
p = &s[0];
p = std::copy_n(host.data(), hostlen, p);
}
*p++ = ':';
std::copy_n(serv.data(), servlen, p);
return s;
}
2015-08-13 11:01:37 +02:00
static int STDERR_COPY = -1;
static int STDOUT_COPY = -1;
void store_original_fds() {
// consider dup'ing stdout too
STDERR_COPY = dup(STDERR_FILENO);
STDOUT_COPY = STDOUT_FILENO;
// no race here, since it is called early
make_socket_closeonexec(STDERR_COPY);
}
2015-10-15 14:42:11 +02:00
void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); }
void close_log_file(int &fd) {
2015-08-13 11:01:37 +02:00
if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) {
close(fd);
}
fd = -1;
}
int open_log_file(const char *path) {
2015-08-12 20:11:20 +02:00
if (strcmp(path, "/dev/stdout") == 0 ||
strcmp(path, "/proc/self/fd/1") == 0) {
2015-08-13 11:01:37 +02:00
return STDOUT_COPY;
}
2015-08-12 20:11:20 +02:00
if (strcmp(path, "/dev/stderr") == 0 ||
strcmp(path, "/proc/self/fd/2") == 0) {
2015-08-13 11:01:37 +02:00
return STDERR_COPY;
}
2015-08-12 20:11:20 +02:00
#if defined O_CLOEXEC
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
#else // !O_CLOEXEC
2014-11-27 15:39:04 +01:00
auto fd =
open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
// We get race condition if execve is called at the same time.
2014-11-27 15:39:04 +01:00
if (fd != -1) {
make_socket_closeonexec(fd);
}
#endif // !O_CLOEXEC
2014-11-27 15:39:04 +01:00
if (fd == -1) {
return -1;
}
return fd;
}
2014-11-27 15:39:04 +01:00
std::string ascii_dump(const uint8_t *data, size_t len) {
std::string res;
2014-11-27 15:39:04 +01:00
for (size_t i = 0; i < len; ++i) {
auto c = data[i];
2014-11-27 15:39:04 +01:00
if (c >= 0x20 && c < 0x7f) {
res += c;
} else {
res += '.';
}
}
return res;
}
2014-11-27 15:39:04 +01:00
char *get_exec_path(int argc, char **const argv, const char *cwd) {
if (argc == 0 || cwd == nullptr) {
return nullptr;
}
auto argv0 = argv[0];
auto len = strlen(argv0);
char *path;
2014-11-27 15:39:04 +01:00
if (argv0[0] == '/') {
path = static_cast<char *>(malloc(len + 1));
2015-04-15 14:18:39 +02:00
if (path == nullptr) {
2015-04-23 17:16:14 +02:00
return nullptr;
2015-04-15 02:20:45 +02:00
}
memcpy(path, argv0, len + 1);
} else {
auto cwdlen = strlen(cwd);
2014-11-27 15:39:04 +01:00
path = static_cast<char *>(malloc(len + 1 + cwdlen + 1));
2015-04-15 14:18:39 +02:00
if (path == nullptr) {
2015-04-23 17:16:14 +02:00
return nullptr;
2015-04-15 02:20:45 +02:00
}
memcpy(path, cwd, cwdlen);
path[cwdlen] = '/';
memcpy(path + cwdlen + 1, argv0, len + 1);
}
return path;
}
2014-11-27 15:39:04 +01:00
bool check_path(const std::string &path) {
// We don't like '\' in path.
return !path.empty() && path[0] == '/' &&
2014-11-27 15:39:04 +01:00
path.find('\\') == std::string::npos &&
path.find("/../") == std::string::npos &&
path.find("/./") == std::string::npos &&
!util::ends_with_l(path, "/..") && !util::ends_with_l(path, "/.");
}
2014-11-27 15:39:04 +01:00
int64_t to_time64(const timeval &tv) {
return tv.tv_sec * 1000000 + tv.tv_usec;
}
bool check_h2_is_selected(const StringRef &proto) {
2016-03-24 17:41:06 +01:00
return streq(NGHTTP2_H2, proto) || streq(NGHTTP2_H2_16, proto) ||
streq(NGHTTP2_H2_14, proto);
}
namespace {
bool select_proto(const unsigned char **out, unsigned char *outlen,
2016-03-24 17:41:06 +01:00
const unsigned char *in, unsigned int inlen,
const StringRef &key) {
for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) {
if (std::equal(std::begin(key), std::end(key), p)) {
*out = p + 1;
*outlen = *p;
return true;
}
}
return false;
}
} // namespace
bool select_h2(const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen) {
2016-03-24 17:41:06 +01:00
return select_proto(out, outlen, in, inlen, NGHTTP2_H2_ALPN) ||
select_proto(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN) ||
select_proto(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN);
2014-11-14 15:14:39 +01:00
}
bool select_protocol(const unsigned char **out, unsigned char *outlen,
2015-10-15 14:42:11 +02:00
const unsigned char *in, unsigned int inlen,
std::vector<std::string> proto_list) {
for (const auto &proto : proto_list) {
2016-03-24 17:41:06 +01:00
if (select_proto(out, outlen, in, inlen, StringRef{proto})) {
return true;
}
}
return false;
}
2014-11-27 15:39:04 +01:00
std::vector<unsigned char> get_default_alpn() {
2016-03-24 17:41:06 +01:00
auto res = std::vector<unsigned char>(NGHTTP2_H2_ALPN.size() +
NGHTTP2_H2_16_ALPN.size() +
NGHTTP2_H2_14_ALPN.size());
auto p = std::begin(res);
2016-03-24 17:41:06 +01:00
p = std::copy_n(std::begin(NGHTTP2_H2_ALPN), NGHTTP2_H2_ALPN.size(), p);
p = std::copy_n(std::begin(NGHTTP2_H2_16_ALPN), NGHTTP2_H2_16_ALPN.size(), p);
p = std::copy_n(std::begin(NGHTTP2_H2_14_ALPN), NGHTTP2_H2_14_ALPN.size(), p);
2014-11-14 15:14:39 +01:00
return res;
}
std::vector<StringRef> split_str(const StringRef &s, char delim) {
size_t len = 1;
auto last = std::end(s);
2016-05-31 14:26:21 +02:00
StringRef::const_iterator d;
for (auto first = std::begin(s); (d = std::find(first, last, delim)) != last;
++len, first = d + 1)
;
auto list = std::vector<StringRef>(len);
len = 0;
for (auto first = std::begin(s);; ++len) {
auto stop = std::find(first, last, delim);
list[len] = StringRef{first, stop};
if (stop == last) {
break;
}
first = stop + 1;
}
return list;
}
std::vector<std::string> parse_config_str_list(const StringRef &s, char delim) {
auto sublist = split_str(s, delim);
auto res = std::vector<std::string>();
res.reserve(sublist.size());
for (const auto &s : sublist) {
res.emplace_back(std::begin(s), std::end(s));
}
return res;
}
int make_socket_closeonexec(int fd) {
int flags;
int rv;
while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR)
;
while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR)
;
return rv;
}
2015-01-05 07:59:51 +01:00
int make_socket_nonblocking(int fd) {
int flags;
int rv;
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
;
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
;
return rv;
}
int make_socket_nodelay(int fd) {
int val = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&val),
sizeof(val)) == -1) {
return -1;
}
return 0;
}
int create_nonblock_socket(int family) {
#ifdef SOCK_NONBLOCK
auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd == -1) {
return -1;
}
#else // !SOCK_NONBLOCK
auto fd = socket(family, SOCK_STREAM, 0);
if (fd == -1) {
return -1;
}
make_socket_nonblocking(fd);
make_socket_closeonexec(fd);
#endif // !SOCK_NONBLOCK
if (family == AF_INET || family == AF_INET6) {
make_socket_nodelay(fd);
}
return fd;
}
bool check_socket_connected(int fd) {
int error;
socklen_t len = sizeof(error);
2016-04-23 17:42:11 +02:00
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
return false;
}
2016-04-23 17:42:11 +02:00
return error == 0;
}
int get_socket_error(int fd) {
int error;
socklen_t len = sizeof(error);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
return -1;
}
return error;
}
bool ipv6_numeric_addr(const char *host) {
uint8_t dst[16];
return inet_pton(AF_INET6, host, dst) == 1;
}
namespace {
std::pair<int64_t, size_t> parse_uint_digits(const void *ss, size_t len) {
const uint8_t *s = static_cast<const uint8_t *>(ss);
int64_t n = 0;
size_t i;
if (len == 0) {
return {-1, 0};
}
constexpr int64_t max = std::numeric_limits<int64_t>::max();
for (i = 0; i < len; ++i) {
if ('0' <= s[i] && s[i] <= '9') {
if (n > max / 10) {
return {-1, 0};
}
n *= 10;
if (n > max - (s[i] - '0')) {
return {-1, 0};
}
n += s[i] - '0';
continue;
}
break;
}
if (i == 0) {
return {-1, 0};
}
return {n, i};
}
} // namespace
int64_t parse_uint_with_unit(const char *s) {
return parse_uint_with_unit(reinterpret_cast<const uint8_t *>(s), strlen(s));
}
int64_t parse_uint_with_unit(const StringRef &s) {
return parse_uint_with_unit(s.byte(), s.size());
}
int64_t parse_uint_with_unit(const uint8_t *s, size_t len) {
int64_t n;
size_t i;
std::tie(n, i) = parse_uint_digits(s, len);
if (n == -1) {
return -1;
}
if (i == len) {
return n;
}
if (i + 1 != len) {
return -1;
}
int mul = 1;
switch (s[i]) {
case 'K':
case 'k':
mul = 1 << 10;
break;
case 'M':
case 'm':
mul = 1 << 20;
break;
case 'G':
case 'g':
mul = 1 << 30;
break;
default:
return -1;
}
constexpr int64_t max = std::numeric_limits<int64_t>::max();
if (n > max / mul) {
return -1;
}
return n * mul;
}
int64_t parse_uint(const char *s) {
return parse_uint(reinterpret_cast<const uint8_t *>(s), strlen(s));
}
int64_t parse_uint(const std::string &s) {
return parse_uint(reinterpret_cast<const uint8_t *>(s.c_str()), s.size());
}
int64_t parse_uint(const StringRef &s) {
return parse_uint(s.byte(), s.size());
}
int64_t parse_uint(const uint8_t *s, size_t len) {
int64_t n;
size_t i;
std::tie(n, i) = parse_uint_digits(s, len);
if (n == -1 || i != len) {
return -1;
}
return n;
}
double parse_duration_with_unit(const char *s) {
return parse_duration_with_unit(reinterpret_cast<const uint8_t *>(s),
strlen(s));
}
double parse_duration_with_unit(const StringRef &s) {
return parse_duration_with_unit(s.byte(), s.size());
}
double parse_duration_with_unit(const uint8_t *s, size_t len) {
2015-03-30 16:20:40 +02:00
constexpr auto max = std::numeric_limits<int64_t>::max();
int64_t n;
size_t i;
std::tie(n, i) = parse_uint_digits(s, len);
if (n == -1) {
goto fail;
}
if (i == len) {
return static_cast<double>(n);
}
switch (s[i]) {
case 'S':
case 's':
2015-03-30 16:20:40 +02:00
// seconds
if (i + 1 != len) {
goto fail;
}
return static_cast<double>(n);
case 'M':
case 'm':
2015-03-30 16:20:40 +02:00
if (i + 1 == len) {
// minutes
if (n > max / 60) {
goto fail;
}
return static_cast<double>(n) * 60;
}
if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) {
goto fail;
}
2015-03-30 16:20:40 +02:00
// milliseconds
return static_cast<double>(n) / 1000.;
2015-03-30 16:20:40 +02:00
case 'H':
case 'h':
// hours
if (i + 1 != len) {
goto fail;
}
if (n > max / 3600) {
goto fail;
}
return static_cast<double>(n) * 3600;
}
fail:
return std::numeric_limits<double>::infinity();
}
std::string duration_str(double t) {
if (t == 0.) {
return "0";
}
auto frac = static_cast<int64_t>(t * 1000) % 1000;
if (frac > 0) {
return utos(static_cast<int64_t>(t * 1000)) + "ms";
}
2015-03-30 16:20:40 +02:00
auto v = static_cast<int64_t>(t);
if (v % 60) {
return utos(v) + "s";
}
v /= 60;
if (v % 60) {
return utos(v) + "m";
}
v /= 60;
return utos(v) + "h";
}
std::string format_duration(const std::chrono::microseconds &u) {
const char *unit = "us";
int d = 0;
auto t = u.count();
if (t >= 1000000) {
d = 1000000;
unit = "s";
} else if (t >= 1000) {
d = 1000;
unit = "ms";
} else {
return utos(t) + unit;
}
return dtos(static_cast<double>(t) / d) + unit;
}
std::string format_duration(double t) {
const char *unit = "us";
if (t >= 1.) {
unit = "s";
} else if (t >= 0.001) {
t *= 1000.;
unit = "ms";
} else {
t *= 1000000.;
return utos(static_cast<int64_t>(t)) + unit;
}
return dtos(t) + unit;
}
std::string dtos(double n) {
auto f = utos(static_cast<int64_t>(round(100. * n)) % 100);
return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f;
}
std::string make_http_hostport(const StringRef &host, uint16_t port) {
if (port != 80 && port != 443) {
return make_hostport(host, port);
}
auto ipv6 = ipv6_numeric_addr(host.c_str());
std::string hostport;
hostport.resize(host.size() + (ipv6 ? 2 : 0));
auto p = &hostport[0];
if (ipv6) {
*p++ = '[';
}
p = std::copy_n(host.c_str(), host.size(), p);
if (ipv6) {
*p++ = ']';
}
return hostport;
}
std::string make_hostport(const StringRef &host, uint16_t port) {
auto ipv6 = ipv6_numeric_addr(host.c_str());
auto serv = utos(port);
std::string hostport;
hostport.resize(host.size() + (ipv6 ? 2 : 0) + 1 + serv.size());
auto p = &hostport[0];
if (ipv6) {
*p++ = '[';
}
p = std::copy_n(host.c_str(), host.size(), p);
if (ipv6) {
*p++ = ']';
}
*p++ = ':';
std::copy_n(serv.c_str(), serv.size(), p);
return hostport;
}
namespace {
void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) {
auto stop = std::min(first + 8, last);
for (auto k = first; k != stop; ++k) {
fprintf(out, "%02x ", *k);
}
2015-03-24 13:43:28 +01:00
// each byte needs 3 spaces (2 hex value and space)
for (; stop != first + 8; ++stop) {
fputs(" ", out);
}
// we have extra space after 8 bytes
fputc(' ', out);
}
} // namespace
void hexdump(FILE *out, const uint8_t *src, size_t len) {
if (len == 0) {
return;
}
size_t buflen = 0;
auto repeated = false;
std::array<uint8_t, 16> buf{};
auto end = src + len;
auto i = src;
for (;;) {
2015-03-24 17:27:18 +01:00
auto nextlen =
std::min(static_cast<size_t>(16), static_cast<size_t>(end - i));
if (nextlen == buflen &&
std::equal(std::begin(buf), std::begin(buf) + buflen, i)) {
2015-03-24 13:43:28 +01:00
// as long as adjacent 16 bytes block are the same, we just
// print single '*'.
if (!repeated) {
repeated = true;
2015-03-24 13:43:28 +01:00
fputs("*\n", out);
}
i += nextlen;
continue;
}
repeated = false;
fprintf(out, "%08lx", static_cast<unsigned long>(i - src));
if (i == end) {
2015-03-24 13:43:28 +01:00
fputc('\n', out);
break;
}
2015-03-24 13:43:28 +01:00
fputs(" ", out);
hexdump8(out, i, end);
hexdump8(out, i + 8, std::max(i + 8, end));
2015-03-24 13:43:28 +01:00
fputc('|', out);
auto stop = std::min(i + 16, end);
buflen = stop - i;
auto p = buf.data();
for (; i != stop; ++i) {
*p++ = *i;
if (0x20 <= *i && *i <= 0x7e) {
2015-03-24 13:43:28 +01:00
fputc(*i, out);
} else {
2015-03-24 13:43:28 +01:00
fputc('.', out);
}
}
2015-03-24 13:43:28 +01:00
fputs("|\n", out);
}
}
void put_uint16be(uint8_t *buf, uint16_t n) {
uint16_t x = htons(n);
memcpy(buf, &x, sizeof(uint16_t));
}
void put_uint32be(uint8_t *buf, uint32_t n) {
uint32_t x = htonl(n);
memcpy(buf, &x, sizeof(uint32_t));
}
uint16_t get_uint16(const uint8_t *data) {
uint16_t n;
memcpy(&n, data, sizeof(uint16_t));
return ntohs(n);
}
uint32_t get_uint32(const uint8_t *data) {
uint32_t n;
memcpy(&n, data, sizeof(uint32_t));
return ntohl(n);
}
uint64_t get_uint64(const uint8_t *data) {
uint64_t n = 0;
n += static_cast<uint64_t>(data[0]) << 56;
n += static_cast<uint64_t>(data[1]) << 48;
n += static_cast<uint64_t>(data[2]) << 40;
n += static_cast<uint64_t>(data[3]) << 32;
2015-08-11 16:42:32 +02:00
n += static_cast<uint64_t>(data[4]) << 24;
n += data[5] << 16;
n += data[6] << 8;
n += data[7];
return n;
}
int read_mime_types(std::map<std::string, std::string> &res,
const char *filename) {
std::ifstream infile(filename);
if (!infile) {
return -1;
}
auto delim_pred = [](char c) { return c == ' ' || c == '\t'; };
std::string line;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
auto type_end = std::find_if(std::begin(line), std::end(line), delim_pred);
if (type_end == std::begin(line)) {
continue;
}
auto ext_end = type_end;
for (;;) {
auto ext_start = std::find_if_not(ext_end, std::end(line), delim_pred);
if (ext_start == std::end(line)) {
break;
}
ext_end = std::find_if(ext_start, std::end(line), delim_pred);
2015-12-01 13:21:50 +01:00
#ifdef HAVE_STD_MAP_EMPLACE
res.emplace(std::string(ext_start, ext_end),
std::string(std::begin(line), type_end));
2015-12-01 13:21:50 +01:00
#else // !HAVE_STD_MAP_EMPLACE
res.insert(std::make_pair(std::string(ext_start, ext_end),
std::string(std::begin(line), type_end)));
#endif // !HAVE_STD_MAP_EMPLACE
}
}
return 0;
}
2016-03-19 10:33:36 +01:00
StringRef percent_decode(BlockAllocator &balloc, const StringRef &src) {
auto iov = make_byte_ref(balloc, src.size() * 3 + 1);
auto p = iov.base;
for (auto first = std::begin(src); first != std::end(src); ++first) {
if (*first != '%') {
*p++ = *first;
continue;
}
if (first + 1 != std::end(src) && first + 2 != std::end(src) &&
is_hex_digit(*(first + 1)) && is_hex_digit(*(first + 2))) {
*p++ = (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2));
first += 2;
continue;
}
*p++ = *first;
}
*p = '\0';
return StringRef{iov.base, p};
}
// Returns x**y
double int_pow(double x, size_t y) {
auto res = 1.;
for (; y; --y) {
res *= x;
}
return res;
}
uint32_t hash32(const StringRef &s) {
/* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */
uint32_t h = 2166136261u;
size_t i;
for (i = 0; i < s.size(); ++i) {
h ^= s[i];
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
}
return h;
}
#if !OPENSSL_1_1_API
namespace {
EVP_MD_CTX *EVP_MD_CTX_new(void) { return EVP_MD_CTX_create(); }
} // namespace
namespace {
void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); }
} // namespace
#endif // !OPENSSL_1_1_API
int sha256(uint8_t *res, const StringRef &s) {
int rv;
auto ctx = EVP_MD_CTX_new();
if (ctx == nullptr) {
return -1;
}
auto ctx_deleter = defer(EVP_MD_CTX_free, ctx);
rv = EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
if (rv != 1) {
return -1;
}
rv = EVP_DigestUpdate(ctx, s.c_str(), s.size());
if (rv != 1) {
return -1;
}
unsigned int mdlen = 32;
rv = EVP_DigestFinal_ex(ctx, res, &mdlen);
if (rv != 1) {
return -1;
}
return 0;
}
2012-01-29 16:34:10 +01:00
} // namespace util
2013-07-12 17:19:03 +02:00
} // namespace nghttp2