Skip to content

Commit 9d8ddc1

Browse files
committed
Reject non-IPv4 hostnames that end in numbers.
1 parent 5b6f280 commit 9d8ddc1

File tree

3 files changed

+39
-12
lines changed

3 files changed

+39
-12
lines changed

src/node_url.cc

+36-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "node_errors.h"
44
#include "node_external_reference.h"
55
#include "node_i18n.h"
6+
#include "util.h"
67
#include "util-inl.h"
78

89
#include <cmath>
@@ -58,7 +59,7 @@ class URLHost {
5859
public:
5960
~URLHost();
6061

61-
void ParseIPv4Host(const char* input, size_t length, bool* is_ipv4);
62+
void ParseIPv4Host(const char* input, size_t length);
6263
void ParseIPv6Host(const char* input, size_t length);
6364
void ParseOpaqueHost(const char* input, size_t length);
6465
void ParseHost(const char* input,
@@ -401,9 +402,37 @@ int64_t ParseNumber(const char* start, const char* end) {
401402
return strtoll(start, nullptr, R);
402403
}
403404

404-
void URLHost::ParseIPv4Host(const char* input, size_t length, bool* is_ipv4) {
405+
// https://url.spec.whatwg.org/#ends-in-a-number-checker
406+
bool EndsInANumber(std::string& str) {
407+
std::vector<std::string> parts = SplitString(str, '.', false);
408+
if (parts.size() == 0)
409+
return false;
410+
411+
if (parts.back() == "") {
412+
if (parts.size() == 1)
413+
return false;
414+
parts.pop_back();
415+
}
416+
417+
const std::string& last = parts.back();
418+
419+
if (last == "")
420+
return false;
421+
422+
int64_t num = ParseNumber(last.c_str(), last.c_str() + last.size());
423+
if (num >= 0)
424+
return true;
425+
426+
if (last.find_first_not_of("0123456789") == std::string::npos) {
427+
return true;
428+
}
429+
430+
return false;
431+
}
432+
433+
434+
void URLHost::ParseIPv4Host(const char* input, size_t length) {
405435
CHECK_EQ(type_, HostType::H_FAILED);
406-
*is_ipv4 = false;
407436
const char* pointer = input;
408437
const char* mark = input;
409438
const char* end = pointer + length;
@@ -436,7 +465,6 @@ void URLHost::ParseIPv4Host(const char* input, size_t length, bool* is_ipv4) {
436465
pointer++;
437466
}
438467
CHECK_GT(parts, 0);
439-
*is_ipv4 = true;
440468

441469
// If any but the last item in numbers is greater than 255, return failure.
442470
// If the last item in numbers is greater than or equal to
@@ -508,11 +536,10 @@ void URLHost::ParseHost(const char* input,
508536
}
509537
}
510538

511-
// Check to see if it's an IPv4 IP address
512-
bool is_ipv4;
513-
ParseIPv4Host(decoded.c_str(), decoded.length(), &is_ipv4);
514-
if (is_ipv4)
515-
return;
539+
// If domain ends in a number, then return the result of IPv4 parsing domain
540+
if (EndsInANumber(decoded)) {
541+
return ParseIPv4Host(decoded.c_str(), decoded.length());
542+
}
516543

517544
// If the unicode flag is set, run the result through punycode ToUnicode
518545
if (unicode && !ToUnicode(decoded, &decoded))

src/util.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,15 @@ std::string GetHumanReadableProcessName() {
164164
return SPrintF("%s[%d]", GetProcessTitle("Node.js"), uv_os_getpid());
165165
}
166166

167-
std::vector<std::string> SplitString(const std::string& in, char delim) {
167+
std::vector<std::string> SplitString(const std::string& in, char delim, bool skipEmpty) {
168168
std::vector<std::string> out;
169169
if (in.empty())
170170
return out;
171171
std::istringstream in_stream(in);
172172
while (in_stream.good()) {
173173
std::string item;
174174
std::getline(in_stream, item, delim);
175-
if (item.empty()) continue;
175+
if (item.empty() && skipEmpty) continue;
176176
out.emplace_back(std::move(item));
177177
}
178178
return out;

src/util.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ struct FunctionDeleter {
645645
template <typename T, void (*function)(T*)>
646646
using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;
647647

648-
std::vector<std::string> SplitString(const std::string& in, char delim);
648+
std::vector<std::string> SplitString(const std::string& in, char delim, bool skipEmpty = true);
649649

650650
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
651651
std::string_view str,

0 commit comments

Comments
 (0)