From 9d8ddc1f01e128daea2848f89e0040299106150c Mon Sep 17 00:00:00 2001 From: Feng Yu Date: Sat, 4 Jun 2022 00:25:47 +0800 Subject: [PATCH] Reject non-IPv4 hostnames that end in numbers. --- src/node_url.cc | 45 ++++++++++++++++++++++++++++++++++++--------- src/util.cc | 4 ++-- src/util.h | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/node_url.cc b/src/node_url.cc index d55034dcafe17d9..a27e40f78143cd1 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -3,6 +3,7 @@ #include "node_errors.h" #include "node_external_reference.h" #include "node_i18n.h" +#include "util.h" #include "util-inl.h" #include @@ -58,7 +59,7 @@ class URLHost { public: ~URLHost(); - void ParseIPv4Host(const char* input, size_t length, bool* is_ipv4); + void ParseIPv4Host(const char* input, size_t length); void ParseIPv6Host(const char* input, size_t length); void ParseOpaqueHost(const char* input, size_t length); void ParseHost(const char* input, @@ -401,9 +402,37 @@ int64_t ParseNumber(const char* start, const char* end) { return strtoll(start, nullptr, R); } -void URLHost::ParseIPv4Host(const char* input, size_t length, bool* is_ipv4) { +// https://url.spec.whatwg.org/#ends-in-a-number-checker +bool EndsInANumber(std::string& str) { + std::vector parts = SplitString(str, '.', false); + if (parts.size() == 0) + return false; + + if (parts.back() == "") { + if (parts.size() == 1) + return false; + parts.pop_back(); + } + + const std::string& last = parts.back(); + + if (last == "") + return false; + + int64_t num = ParseNumber(last.c_str(), last.c_str() + last.size()); + if (num >= 0) + return true; + + if (last.find_first_not_of("0123456789") == std::string::npos) { + return true; + } + + return false; +} + + +void URLHost::ParseIPv4Host(const char* input, size_t length) { CHECK_EQ(type_, HostType::H_FAILED); - *is_ipv4 = false; const char* pointer = input; const char* mark = input; const char* end = pointer + length; @@ -436,7 +465,6 @@ void URLHost::ParseIPv4Host(const char* input, size_t length, bool* is_ipv4) { pointer++; } CHECK_GT(parts, 0); - *is_ipv4 = true; // If any but the last item in numbers is greater than 255, return failure. // If the last item in numbers is greater than or equal to @@ -508,11 +536,10 @@ void URLHost::ParseHost(const char* input, } } - // Check to see if it's an IPv4 IP address - bool is_ipv4; - ParseIPv4Host(decoded.c_str(), decoded.length(), &is_ipv4); - if (is_ipv4) - return; + // If domain ends in a number, then return the result of IPv4 parsing domain + if (EndsInANumber(decoded)) { + return ParseIPv4Host(decoded.c_str(), decoded.length()); + } // If the unicode flag is set, run the result through punycode ToUnicode if (unicode && !ToUnicode(decoded, &decoded)) diff --git a/src/util.cc b/src/util.cc index bae5a47a06d8dee..90f84049b52368f 100644 --- a/src/util.cc +++ b/src/util.cc @@ -164,7 +164,7 @@ std::string GetHumanReadableProcessName() { return SPrintF("%s[%d]", GetProcessTitle("Node.js"), uv_os_getpid()); } -std::vector SplitString(const std::string& in, char delim) { +std::vector SplitString(const std::string& in, char delim, bool skipEmpty) { std::vector out; if (in.empty()) return out; @@ -172,7 +172,7 @@ std::vector SplitString(const std::string& in, char delim) { while (in_stream.good()) { std::string item; std::getline(in_stream, item, delim); - if (item.empty()) continue; + if (item.empty() && skipEmpty) continue; out.emplace_back(std::move(item)); } return out; diff --git a/src/util.h b/src/util.h index 14c8758c849904f..3c4f236d7d5cef3 100644 --- a/src/util.h +++ b/src/util.h @@ -645,7 +645,7 @@ struct FunctionDeleter { template using DeleteFnPtr = typename FunctionDeleter::Pointer; -std::vector SplitString(const std::string& in, char delim); +std::vector SplitString(const std::string& in, char delim, bool skipEmpty = true); inline v8::MaybeLocal ToV8Value(v8::Local context, std::string_view str,