Packages
sippet
0.3.5
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
An Elixir Session Initiation Protocol (SIP) stack.
Current section
Files
Jump to
Current section
Files
c_src/utils.cc
// Copyright (c) 2017 The Sippet Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Parts of this code was taken from Chromium sources:
// Copyright (c) 2011-2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "utils.h"
#include <string>
#include <iostream>
namespace {
// Utility to convert a character to a digit in a given base
template<typename CHAR, int BASE, bool BASE_LTE_10> class BaseCharToDigit {
};
// Faster specialization for bases <= 10
template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, true> {
public:
static bool Convert(CHAR c, uint8_t* digit) {
if (c >= '0' && c < '0' + BASE) {
*digit = static_cast<uint8_t>(c - '0');
return true;
}
return false;
}
};
// Specialization for bases where 10 < base <= 36
template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, false> {
public:
static bool Convert(CHAR c, uint8_t* digit) {
if (c >= '0' && c <= '9') {
*digit = c - '0';
} else if (c >= 'a' && c < 'a' + BASE - 10) {
*digit = c - 'a' + 10;
} else if (c >= 'A' && c < 'A' + BASE - 10) {
*digit = c - 'A' + 10;
} else {
return false;
}
return true;
}
};
template <int BASE, typename CHAR>
bool CharToDigit(CHAR c, uint8_t* digit) {
return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
}
// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it
// is locale independent, whereas the functions we are replacing were
// locale-dependent. TBD what is desired, but for the moment let's not
// introduce a change in behaviour.
template<typename CHAR> class WhitespaceHelper {
};
template<> class WhitespaceHelper<char> {
public:
static bool Invoke(char c) {
return 0 != isspace(static_cast<unsigned char>(c));
}
};
template<typename CHAR> bool LocalIsWhitespace(CHAR c) {
return WhitespaceHelper<CHAR>::Invoke(c);
}
// IteratorRangeToNumberTraits should provide:
// - a typedef for iterator_type, the iterator type used as input.
// - a typedef for value_type, the target numeric type.
// - static functions min, max (returning the minimum and maximum permitted
// values)
// - constant kBase, the base in which to interpret the input
template<typename IteratorRangeToNumberTraits>
class IteratorRangeToNumber {
public:
typedef IteratorRangeToNumberTraits traits;
typedef typename traits::iterator_type const_iterator;
typedef typename traits::value_type value_type;
// Generalized iterator-range-to-number conversion.
//
static bool Invoke(const_iterator begin,
const_iterator end,
value_type* output) {
bool valid = true;
while (begin != end && LocalIsWhitespace(*begin)) {
valid = false;
++begin;
}
if (begin != end && *begin == '-') {
if (!std::numeric_limits<value_type>::is_signed) {
*output = 0;
valid = false;
} else if (!Negative::Invoke(begin + 1, end, output)) {
valid = false;
}
} else {
if (begin != end && *begin == '+') {
++begin;
}
if (!Positive::Invoke(begin, end, output)) {
valid = false;
}
}
return valid;
}
private:
// Sign provides:
// - a static function, CheckBounds, that determines whether the next digit
// causes an overflow/underflow
// - a static function, Increment, that appends the next digit appropriately
// according to the sign of the number being parsed.
template<typename Sign>
class Base {
public:
static bool Invoke(const_iterator begin, const_iterator end,
typename traits::value_type* output) {
*output = 0;
if (begin == end) {
return false;
}
// Note: no performance difference was found when using template
// specialization to remove this check in bases other than 16
if (traits::kBase == 16 && end - begin > 2 && *begin == '0' &&
(*(begin + 1) == 'x' || *(begin + 1) == 'X')) {
begin += 2;
}
for (const_iterator current = begin; current != end; ++current) {
uint8_t new_digit = 0;
if (!CharToDigit<traits::kBase>(*current, &new_digit)) {
return false;
}
if (current != begin) {
if (!Sign::CheckBounds(output, new_digit)) {
return false;
}
*output *= traits::kBase;
}
Sign::Increment(new_digit, output);
}
return true;
}
};
class Positive : public Base<Positive> {
public:
static bool CheckBounds(value_type* output, uint8_t new_digit) {
if (*output > static_cast<value_type>(traits::max() / traits::kBase) ||
(*output == static_cast<value_type>(traits::max() / traits::kBase) &&
new_digit > traits::max() % traits::kBase)) {
*output = traits::max();
return false;
}
return true;
}
static void Increment(uint8_t increment, value_type* output) {
*output += increment;
}
};
class Negative : public Base<Negative> {
public:
static bool CheckBounds(value_type* output, uint8_t new_digit) {
if (*output < traits::min() / traits::kBase ||
(*output == traits::min() / traits::kBase &&
new_digit > 0 - traits::min() % traits::kBase)) {
*output = traits::min();
return false;
}
return true;
}
static void Increment(uint8_t increment, value_type* output) {
*output -= increment;
}
};
};
template<typename ITERATOR, typename VALUE, int BASE>
class BaseIteratorRangeToNumberTraits {
public:
typedef ITERATOR iterator_type;
typedef VALUE value_type;
static value_type min() {
return std::numeric_limits<value_type>::min();
}
static value_type max() {
return std::numeric_limits<value_type>::max();
}
static const int kBase = BASE;
};
template <typename VALUE, int BASE>
class StringPieceToNumberTraits
: public BaseIteratorRangeToNumberTraits<StringPiece::const_iterator,
VALUE,
BASE> {
};
template <typename VALUE>
bool StringToIntImpl(const StringPiece& input, VALUE* output) {
return IteratorRangeToNumber<StringPieceToNumberTraits<VALUE, 10> >::Invoke(
input.begin(), input.end(), output);
}
// See RFC 2616 Sec 2.2 for the definition of |token|.
template<typename It>
bool IsTokenImpl(It begin, It end) {
if (begin == end)
return false;
for (It iter = begin; iter != end; ++iter) {
if (!IsTokenChar(*iter))
return false;
}
return true;
}
bool UnquoteImpl(std::string::const_iterator begin,
std::string::const_iterator end,
bool strict_quotes,
std::string* out) {
// Empty string
if (begin == end)
return false;
// Nothing to unquote.
if (!IsQuote(*begin))
return false;
// Anything other than double quotes in strict mode.
if (strict_quotes && *begin != '"')
return false;
// No terminal quote mark.
if (end - begin < 2 || *begin != *(end - 1))
return false;
char quote = *begin;
// Strip quotemarks
++begin;
--end;
// Unescape quoted-pair (defined in RFC 2616 section 2.2)
bool prev_escape = false;
std::string unescaped;
for (; begin != end; ++begin) {
char c = *begin;
if (c == '\\' && !prev_escape) {
prev_escape = true;
continue;
}
if (strict_quotes && !prev_escape && c == quote)
return false;
prev_escape = false;
unescaped.push_back(c);
}
// Terminal quote is escaped.
if (strict_quotes && prev_escape)
return false;
*out = std::move(unescaped);
return true;
}
bool StrictUnquote(std::string::const_iterator begin,
std::string::const_iterator end,
std::string* out) {
return UnquoteImpl(begin, end, true, out);
}
} // namespace
bool IsTokenChar(unsigned char c) {
return !(c >= 0x80 || c <= 0x1F || c == 0x7F || c == '(' || c == ')' ||
c == '<' || c == '>' || c == '@' || c == ',' || c == ';' ||
c == ':' || c == '\\' || c == '"' || c == '/' || c == '[' ||
c == ']' || c == '?' || c == '=' || c == '{' || c == '}' ||
c == ' ' || c == '\t');
}
bool IsToken(std::string::const_iterator begin,
std::string::const_iterator end) {
return IsTokenImpl(begin, end);
}
bool IsToken(StringPiece::const_iterator begin,
StringPiece::const_iterator end) {
return IsTokenImpl(begin, end);
}
// ASCII-specific tolower. The standard library's tolower is locale sensitive,
// so we don't want to use it here.
char ToLowerASCII(char c) {
return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
}
std::string ToLowerASCII(StringPiece str) {
std::string ret;
ret.reserve(str.size());
for (size_t i = 0; i < str.size(); i++)
ret.push_back(ToLowerASCII(str[i]));
return ret;
}
// Implementation note: Normally this function will be called with a hardcoded
// constant for the lowercase_ascii parameter. Constructing a StringPiece from
// a C constant requires running strlen, so the result will be two passes
// through the buffers, one to file the length of lowercase_ascii, and one to
// compare each letter.
//
// This function could have taken a const char* to avoid this and only do one
// pass through the string. But the strlen is faster than the case-insensitive
// compares and lets us early-exit in the case that the strings are different
// lengths (will often be the case for non-matches). So whether one approach or
// the other will be faster depends on the case.
//
// The hardcoded strings are typically very short so it doesn't matter, and the
// string piece gives additional flexibility for the caller (doesn't have to be
// null terminated) so we choose the StringPiece route.
bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) {
if (str.size() != lowercase_ascii.size())
return false;
for (size_t i = 0; i < str.size(); i++) {
if (ToLowerASCII(str[i]) != lowercase_ascii[i])
return false;
}
return true;
}
bool StringToInt(const StringPiece& input, int* output) {
return StringToIntImpl(input, output);
}
bool StringToDouble(const StringPiece& input, double* output) {
try {
*output = stod(input.as_string());
return true;
} catch (const std::exception& e) {
return false;
}
}
bool IsLWS(char c) {
return strchr(SIP_LWS, c) != NULL;
}
void TrimLWS(std::string::const_iterator* begin,
std::string::const_iterator* end) {
// leading whitespace
while (*begin < *end && IsLWS((*begin)[0]))
++(*begin);
// trailing whitespace
while (*begin < *end && IsLWS((*end)[-1]))
--(*end);
}
bool IsQuote(char c) {
// Single quote mark isn't actually part of quoted-text production,
// but apparently some servers rely on this.
return c == '"' || c == '\'';
}
std::string Unquote(std::string::const_iterator begin,
std::string::const_iterator end) {
std::string result;
if (!UnquoteImpl(begin, end, false, &result))
return std::string(begin, end);
return result;
}
bool ParseHostAndPort(std::string::const_iterator host_and_port_begin,
std::string::const_iterator host_and_port_end,
std::string* host,
int* port) {
if (host_and_port_begin >= host_and_port_end)
return false;
// hostport = host [ COLON port ]
// host = hostname / IPv4address / IPv6reference
// hostname = *( domainlabel "." ) toplabel [ "." ]
// domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum
// toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
// IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
// IPv6reference = "[" IPv6address "]"
// IPv6address = hexpart [ ":" IPv4address ]
// hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
// hexseq = hex4 *( ":" hex4)
// hex4 = 1*4HEXDIG
// port = 1*DIGIT
std::string::const_iterator host_start = host_and_port_begin, host_end;
if (*host_and_port_begin == '[') {
// parse an IPv6 address
for (; host_and_port_begin < host_and_port_end; host_and_port_begin++) {
if (*host_and_port_begin == ']')
break;
}
if (host_and_port_begin == host_and_port_end)
return false;
host_end = ++host_and_port_begin;
} else {
// parse a hostname or IPv4 address
for (; host_and_port_begin < host_and_port_end; host_and_port_begin++) {
if (*host_and_port_begin == ':')
break;
}
host_end = host_and_port_begin;
}
std::string::const_iterator port_start, port_end;
if (host_and_port_begin < host_and_port_end
&& *host_and_port_begin == ':') {
port_start = ++host_and_port_begin;
for (; host_and_port_begin < host_and_port_end; host_and_port_begin++) {
if (!isdigit(*host_and_port_begin))
return false;
}
port_end = host_and_port_begin;
} else {
port_start = port_end = host_and_port_end;
}
if (host_and_port_begin < host_and_port_end) {
// trailing garbage is considered error
return false;
}
if (port_start < port_end) {
if (!StringToInt(StringPiece(port_start, port_end), port))
return false;
} else {
*port = -1;
}
host->assign(host_start, host_end);
return true;
}
bool ParseHostAndPort(const std::string& host_and_port,
std::string* host,
int* port) {
return ParseHostAndPort(
host_and_port.begin(), host_and_port.end(), host, port);
}
HeadersIterator::HeadersIterator(
std::string::const_iterator headers_begin,
std::string::const_iterator headers_end,
const std::string& line_delimiter)
: lines_(headers_begin, headers_end, line_delimiter) {
}
HeadersIterator::~HeadersIterator() {
}
bool HeadersIterator::GetNext() {
while (lines_.GetNext()) {
name_begin_ = lines_.token_begin();
values_end_ = lines_.token_end();
std::string::const_iterator colon(std::find(name_begin_, values_end_, ':'));
if (colon == values_end_)
continue; // skip malformed header
name_end_ = colon;
// If the name starts with LWS, it is an invalid line.
// Leading LWS implies a line continuation, and these should have
// already been joined by AssembleRawHeaders().
if (name_begin_ == name_end_ || IsLWS(*name_begin_))
continue;
TrimLWS(&name_begin_, &name_end_);
if (!IsToken(name_begin_, name_end_))
continue; // skip malformed header
values_begin_ = colon + 1;
TrimLWS(&values_begin_, &values_end_);
// if we got a header name, then we are done.
return true;
}
return false;
}
bool HeadersIterator::AdvanceTo(const char* name) {
while (GetNext()) {
if (LowerCaseEqualsASCII(StringPiece(name_begin_, name_end_), name)) {
return true;
}
}
return false;
}
ValuesIterator::ValuesIterator(
std::string::const_iterator values_begin,
std::string::const_iterator values_end,
char delimiter)
: values_(values_begin, values_end, std::string(1, delimiter)) {
values_.set_quote_chars("\'\"");
}
ValuesIterator::ValuesIterator(const ValuesIterator& other) = default;
ValuesIterator::~ValuesIterator() {
}
bool ValuesIterator::GetNext() {
while (values_.GetNext()) {
value_begin_ = values_.token_begin();
value_end_ = values_.token_end();
TrimLWS(&value_begin_, &value_end_);
// bypass empty values.
if (value_begin_ != value_end_)
return true;
}
return false;
}
GenericParametersIterator::GenericParametersIterator(
std::string::const_iterator begin,
std::string::const_iterator end)
: props_(begin, end, ';'),
valid_(true),
name_begin_(end),
name_end_(end),
value_begin_(end),
value_end_(end),
value_is_quoted_(false) {
}
GenericParametersIterator::~GenericParametersIterator() {
}
bool GenericParametersIterator::GetNext() {
if (!props_.GetNext())
return false;
value_begin_ = props_.value_begin();
value_end_ = props_.value_end();
name_begin_ = name_end_ = value_end_;
std::string::const_iterator equals =
std::find(value_begin_, value_end_, '=');
if (equals != value_end_ && equals != value_begin_) {
name_begin_ = value_begin_;
name_end_ = equals;
value_begin_ = equals + 1;
} else {
name_begin_ = value_begin_;
name_end_ = value_end_;
value_begin_ = value_end_;
}
TrimLWS(&name_begin_, &name_end_);
TrimLWS(&value_begin_, &value_end_);
value_is_quoted_ = false;
unquoted_value_.clear();
if (value_begin_ != value_end_) {
if (IsQuote(*value_begin_)) {
if (*value_begin_ != *(value_end_ - 1)
|| value_begin_ + 1 == value_end_) {
++value_begin_;
} else {
value_is_quoted_ = true;
unquoted_value_ = Unquote(value_begin_, value_end_);
}
}
}
return true;
}
NameValuePairsIterator::NameValuePairsIterator(
std::string::const_iterator begin,
std::string::const_iterator end,
char delimiter,
Values optional_values,
Quotes strict_quotes)
: props_(begin, end, delimiter),
valid_(true),
name_begin_(end),
name_end_(end),
value_begin_(end),
value_end_(end),
value_is_quoted_(false),
values_optional_(optional_values == Values::NOT_REQUIRED),
strict_quotes_(strict_quotes == Quotes::STRICT_QUOTES) {
if (strict_quotes_)
props_.set_quote_chars("\"");
}
NameValuePairsIterator::NameValuePairsIterator(
std::string::const_iterator begin,
std::string::const_iterator end,
char delimiter)
: NameValuePairsIterator(begin,
end,
delimiter,
Values::REQUIRED,
Quotes::NOT_STRICT) {}
NameValuePairsIterator::NameValuePairsIterator(
const NameValuePairsIterator& other) = default;
NameValuePairsIterator::~NameValuePairsIterator() {}
// We expect properties to be formatted as one of:
// name="value"
// name='value'
// name='\'value\''
// name=value
// name = value
// name (if values_optional_ is true)
// Due to buggy implementations found in some embedded devices, we also
// accept values with missing close quotemark (http://crbug.com/39836):
// name="value
bool NameValuePairsIterator::GetNext() {
if (!props_.GetNext())
return false;
// Set the value as everything. Next we will split out the name.
value_begin_ = props_.value_begin();
value_end_ = props_.value_end();
name_begin_ = name_end_ = value_end_;
// Scan for the equals sign.
std::string::const_iterator equals = std::find(value_begin_, value_end_, '=');
if (equals == value_begin_)
return valid_ = false; // Malformed, no name
if (equals == value_end_ && !values_optional_)
return valid_ = false; // Malformed, no equals sign and values are required
// If an equals sign was found, verify that it wasn't inside of quote marks.
if (equals != value_end_) {
for (std::string::const_iterator it = value_begin_; it != equals; ++it) {
if (IsQuote(*it))
return valid_ = false; // Malformed, quote appears before equals sign
}
}
name_begin_ = value_begin_;
name_end_ = equals;
value_begin_ = (equals == value_end_) ? value_end_ : equals + 1;
TrimLWS(&name_begin_, &name_end_);
TrimLWS(&value_begin_, &value_end_);
value_is_quoted_ = false;
unquoted_value_.clear();
if (equals != value_end_ && value_begin_ == value_end_) {
// Malformed; value is empty
return valid_ = false;
}
if (value_begin_ != value_end_ && IsQuote(*value_begin_)) {
value_is_quoted_ = true;
if (strict_quotes_) {
if (!StrictUnquote(value_begin_, value_end_, &unquoted_value_))
return valid_ = false;
return true;
}
// Trim surrounding quotemarks off the value
if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) {
// NOTE: This is not as graceful as it sounds:
// * quoted-pairs will no longer be unquoted
// (["\"hello] should give ["hello]).
// * Does not detect when the final quote is escaped
// (["value\"] should give [value"])
value_is_quoted_ = false;
++value_begin_; // Gracefully recover from mismatching quotes.
} else {
// Do not store iterators into this. See declaration of unquoted_value_.
unquoted_value_ = Unquote(value_begin_, value_end_);
}
}
return true;
}
bool NameValuePairsIterator::IsQuote(char c) const {
if (strict_quotes_)
return c == '"';
return ::IsQuote(c);
}