1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-07 17:41:00 +01:00

Merge pull request #13943 from NixOS/mergify/bp/2.31-maintenance/pr-13940

libstore: Reallow unbracketed IPv6 addresses in store references (backport #13940)
This commit is contained in:
mergify[bot] 2025-09-08 23:47:51 +00:00 committed by GitHub
commit 1676a04197
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 91 additions and 2 deletions

View file

@ -0,0 +1 @@
ssh://::1

View file

@ -0,0 +1 @@
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e

View file

@ -0,0 +1 @@
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e?a=b&c=d

View file

@ -148,4 +148,39 @@ URI_TEST(
.params = {},
}))
static StoreReference sshLoopbackIPv6{
.variant =
StoreReference::Specified{
.scheme = "ssh",
.authority = "[::1]",
},
};
URI_TEST_READ(ssh_unbracketed_ipv6_1, sshLoopbackIPv6)
static StoreReference sshIPv6AuthorityWithUserinfo{
.variant =
StoreReference::Specified{
.scheme = "ssh",
.authority = "userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e]",
},
};
URI_TEST_READ(ssh_unbracketed_ipv6_2, sshIPv6AuthorityWithUserinfo)
static StoreReference sshIPv6AuthorityWithUserinfoAndParams{
.variant =
StoreReference::Specified{
.scheme = "ssh",
.authority = "userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e]",
},
.params =
{
{"a", "b"},
{"c", "d"},
},
};
URI_TEST_READ(ssh_unbracketed_ipv6_3, sshIPv6AuthorityWithUserinfoAndParams)
} // namespace nix

View file

@ -105,6 +105,7 @@ boost = dependency(
'container',
# Shouldn't list, because can header-only, and Meson currently looks for libs
#'regex',
'url',
],
include_type : 'system',
)

View file

@ -1,11 +1,12 @@
#include <regex>
#include "nix/util/error.hh"
#include "nix/util/split.hh"
#include "nix/util/url.hh"
#include "nix/store/store-reference.hh"
#include "nix/util/file-system.hh"
#include "nix/util/util.hh"
#include <boost/url/ipv6_address.hpp>
namespace nix {
static bool isNonUriPath(const std::string & spec)
@ -43,6 +44,29 @@ std::string StoreReference::render(bool withParams) const
return res;
}
namespace {
struct SchemeAndAuthorityWithPath
{
std::string_view scheme;
std::string_view authority;
};
} // namespace
/**
* Return the 'scheme' and remove the '://' or ':' separator.
*/
static std::optional<SchemeAndAuthorityWithPath> splitSchemePrefixTo(std::string_view string)
{
auto scheme = splitPrefixTo(string, ':');
if (!scheme)
return std::nullopt;
splitPrefix(string, "//");
return SchemeAndAuthorityWithPath{.scheme = *scheme, .authority = string};
}
StoreReference StoreReference::parse(const std::string & uri, const StoreReference::Params & extraParams)
{
auto params = extraParams;
@ -92,6 +116,32 @@ StoreReference StoreReference::parse(const std::string & uri, const StoreReferen
},
.params = std::move(params),
};
} else if (auto schemeAndAuthority = splitSchemePrefixTo(baseURI)) {
/* Back-compatibility shim to accept unbracketed IPv6 addresses after the scheme.
* Old versions of nix allowed that. Note that this is ambiguous and does not allow
* specifying the port number. For that the address must be bracketed, otherwise it's
* greedily assumed to be the part of the host address. */
auto authorityString = schemeAndAuthority->authority;
auto userinfo = splitPrefixTo(authorityString, '@');
auto maybeIpv6 = boost::urls::parse_ipv6_address(authorityString);
if (maybeIpv6) {
std::string fixedAuthority;
if (userinfo) {
fixedAuthority += *userinfo;
fixedAuthority += '@';
}
fixedAuthority += '[';
fixedAuthority += authorityString;
fixedAuthority += ']';
return {
.variant =
Specified{
.scheme = std::string(schemeAndAuthority->scheme),
.authority = fixedAuthority,
},
.params = std::move(params),
};
}
}
}