1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-14 14:32:42 +01:00
nix/src/libstore/store-reference.cc
Sergei Zimmerman 1ca1882e8c libstore: Reallow unbracketed IPv6 addresses in store references
This implements a special back-compat shim to specifically allow
unbracketed IPv6 addresses in store references. This is something
that is relied upon in the wild and the old parsing logic accepted
both ways (brackets were optional). This patch restores this behavior.
As always, we didn't have any tests for this.

Addresses #13937.

(cherry picked from commit 7cc654afa9)
2025-09-08 23:22:41 +00:00

164 lines
5.2 KiB
C++

#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)
{
return
// is not a URL
spec.find("://") == std::string::npos
// Has at least one path separator, and so isn't a single word that
// might be special like "auto"
&& spec.find("/") != std::string::npos;
}
std::string StoreReference::render(bool withParams) const
{
std::string res;
std::visit(
overloaded{
[&](const StoreReference::Auto &) { res = "auto"; },
[&](const StoreReference::Daemon &) { res = "daemon"; },
[&](const StoreReference::Local &) { res = "local"; },
[&](const StoreReference::Specified & g) {
res = g.scheme;
res += "://";
res += g.authority;
},
},
variant);
if (withParams && !params.empty()) {
res += "?";
res += encodeQuery(params);
}
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;
try {
auto parsedUri = parseURL(uri, /*lenient=*/true);
params.insert(parsedUri.query.begin(), parsedUri.query.end());
auto baseURI = parsedUri.authority.value_or(ParsedURL::Authority{}).to_string() + parsedUri.path;
return {
.variant =
Specified{
.scheme = std::move(parsedUri.scheme),
.authority = std::move(baseURI),
},
.params = std::move(params),
};
} catch (BadURL &) {
auto [baseURI, uriParams] = splitUriAndParams(uri);
params.insert(uriParams.begin(), uriParams.end());
if (baseURI == "" || baseURI == "auto") {
return {
.variant = Auto{},
.params = std::move(params),
};
} else if (baseURI == "daemon") {
if (params.empty())
return {.variant = Daemon{}};
return {
.variant = Specified{.scheme = "unix", .authority = ""},
.params = std::move(params),
};
} else if (baseURI == "local") {
if (params.empty())
return {.variant = Local{}};
return {
.variant = Specified{.scheme = "local", .authority = ""},
.params = std::move(params),
};
} else if (isNonUriPath(baseURI)) {
return {
.variant =
Specified{
.scheme = "local",
.authority = absPath(baseURI),
},
.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),
};
}
}
}
throw UsageError("Cannot parse Nix store '%s'", uri);
}
/* Split URI into protocol+hierarchy part and its parameter set. */
std::pair<std::string, StoreReference::Params> splitUriAndParams(const std::string & uri_)
{
auto uri(uri_);
StoreReference::Params params;
auto q = uri.find('?');
if (q != std::string::npos) {
params = decodeQuery(uri.substr(q + 1), /*lenient=*/true);
uri = uri_.substr(0, q);
}
return {uri, params};
}
} // namespace nix