Bug 1641222 - Follow CNAME/AliasForm chains r=dragana,necko-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D79264
This commit is contained in:
Valentin Gosu 2020-06-22 13:24:19 +00:00
Родитель f9d8e206df
Коммит cb869c015c
6 изменённых файлов: 274 добавлений и 5 удалений

Просмотреть файл

@ -832,7 +832,8 @@ nsresult TRR::DohDecode(nsCString& aHost) {
}
uint16_t TYPE = get16bit(mResponse, index);
if ((TYPE != TRRTYPE_CNAME) && (TYPE != static_cast<uint16_t>(mType))) {
if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) &&
(TYPE != static_cast<uint16_t>(mType))) {
// Not the same type as was asked for nor CNAME
LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, mType,
TYPE));
@ -1024,6 +1025,24 @@ nsresult TRR::DohDecode(nsCString& aHost) {
parsed.mSvcFieldValue.AppendElement(value);
}
// Check for AliasForm
if (mCname.IsEmpty() && parsed.mSvcFieldPriority == 0) {
// Alias form SvcDomainName must not have the "." value (empty)
if (parsed.mSvcDomainName.IsEmpty()) {
return NS_ERROR_UNEXPECTED;
}
mCname = parsed.mSvcDomainName;
ToLowerCase(mCname);
LOG(("TRR::DohDecode HTTPSSVC AliasForm host %s => %s\n",
host.get(), mCname.get()));
break;
}
if (mType != TRRTYPE_HTTPSSVC) {
// Ignore the entry that we just parsed if we didn't ask for it.
break;
}
if (!mResult.is<TypeRecordHTTPSSVC>()) {
mResult = mozilla::AsVariant(CopyableTArray<SVCB>());
}
@ -1280,7 +1299,7 @@ nsresult TRR::On200Response(nsIChannel* aChannel) {
if (NS_SUCCEEDED(rv)) {
if (!mDNS.mAddresses.getFirst() && !mCname.IsEmpty() &&
mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
mType != TRRTYPE_TXT) {
nsCString cname = mCname;
LOG(("TRR: check for CNAME record for %s within previous response\n",
cname.get()));

Просмотреть файл

@ -102,13 +102,17 @@ class TRRDNSListener {
const dns = Cc["@mozilla.org/network/dns-service;1"].getService(
Ci.nsIDNSService
);
const threadManager = Cc["@mozilla.org/thread-manager;1"].getService(
Ci.nsIThreadManager
);
const currentThread = threadManager.currentThread;
if (trrServer == "") {
this.request = dns.asyncResolve(
name,
0,
this,
Services.tm.currentThread,
currentThread,
{} // defaultOriginAttributes
);
} else {
@ -118,7 +122,7 @@ class TRRDNSListener {
trrServer,
0,
this,
Services.tm.currentThread,
currentThread,
{} // defaultOriginAttributes
);
Assert.ok(!expectEarlyFail);

Просмотреть файл

@ -205,3 +205,230 @@ add_task(async function testHTTPSSVC() {
Assert.equal(answer[2].name, "hello");
Assert.equal(answer[2].values.length, 0);
});
add_task(async function test_aliasform() {
let trrServer = new TRRServer();
registerCleanupFunction(async () => trrServer.stop());
await trrServer.start();
dump(`port = ${trrServer.port}\n`);
if (inChildProcess()) {
do_send_remote_message("mode3-port", trrServer.port);
await do_await_remote_message("mode3-port-done");
} else {
Services.prefs.setIntPref("network.trr.mode", 3);
Services.prefs.setCharPref(
"network.trr.uri",
`https://foo.example.com:${trrServer.port}/dns-query`
);
}
// Test that HTTPS priority = 0 (AliasForm) behaves like a CNAME
await trrServer.registerDoHAnswers("test.com", "A", [
{
name: "test.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 0,
name: "something.com",
values: [],
},
},
]);
await trrServer.registerDoHAnswers("something.com", "A", [
{
name: "something.com",
ttl: 55,
type: "A",
flush: false,
data: "1.2.3.4",
},
]);
await new TRRDNSListener("test.com", "1.2.3.4");
// Test a chain of HTTPSSVC AliasForm and CNAMEs
await trrServer.registerDoHAnswers("x.com", "A", [
{
name: "x.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 0,
name: "y.com",
values: [],
},
},
]);
await trrServer.registerDoHAnswers("y.com", "A", [
{
name: "y.com",
type: "CNAME",
ttl: 55,
class: "IN",
flush: false,
data: "z.com",
},
]);
await trrServer.registerDoHAnswers("z.com", "A", [
{
name: "z.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 0,
name: "target.com",
values: [],
},
},
]);
await trrServer.registerDoHAnswers("target.com", "A", [
{
name: "target.com",
ttl: 55,
type: "A",
flush: false,
data: "4.3.2.1",
},
]);
await new TRRDNSListener("x.com", "4.3.2.1");
// We get a ServiceForm instead of a A answer, CNAME or AliasForm
await trrServer.registerDoHAnswers("no-ip-host.com", "A", [
{
name: "no-ip-host.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 1,
name: "h3pool",
values: [
{ key: "alpn", value: "h2,h3" },
{ key: "no-default-alpn" },
{ key: "port", value: 8888 },
{ key: "ipv4hint", value: "1.2.3.4" },
{ key: "esniconfig", value: "123..." },
{ key: "ipv6hint", value: "::1" },
],
},
},
]);
let [, , inStatus] = await new TRRDNSListener(
"no-ip-host.com",
undefined,
false
);
Assert.ok(
!Components.isSuccessCode(inStatus),
`${inStatus} should be an error code`
);
// Test CNAME/AliasForm loop
await trrServer.registerDoHAnswers("loop.com", "A", [
{
name: "loop.com",
type: "CNAME",
ttl: 55,
class: "IN",
flush: false,
data: "loop2.com",
},
]);
await trrServer.registerDoHAnswers("loop2.com", "A", [
{
name: "loop2.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 0,
name: "loop.com",
values: [],
},
},
]);
[, , inStatus] = await new TRRDNSListener("loop.com", undefined, false);
Assert.ok(
!Components.isSuccessCode(inStatus),
`${inStatus} should be an error code`
);
// Alias form for .
await trrServer.registerDoHAnswers("empty.com", "A", [
{
name: "empty.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 0,
name: "", // This is not allowed
values: [],
},
},
]);
[, , inStatus] = await new TRRDNSListener("empty.com", undefined, false);
Assert.ok(
!Components.isSuccessCode(inStatus),
`${inStatus} should be an error code`
);
// We should ignore ServiceForm if an AliasForm record is also present
await trrServer.registerDoHAnswers("multi.com", "HTTPSSVC", [
{
name: "multi.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 1,
name: "h3pool",
values: [
{ key: "alpn", value: "h2,h3" },
{ key: "no-default-alpn" },
{ key: "port", value: 8888 },
{ key: "ipv4hint", value: "1.2.3.4" },
{ key: "esniconfig", value: "123..." },
{ key: "ipv6hint", value: "::1" },
],
},
},
{
name: "multi.com",
ttl: 55,
type: "HTTPSSVC",
flush: false,
data: {
priority: 0,
name: "example.com",
values: [],
},
},
]);
let listener = new DNSListener();
let request = dns.asyncResolveByType(
"multi.com",
dns.RESOLVE_TYPE_HTTPSSVC,
0,
listener,
mainThread,
defaultOriginAttributes
);
let [inRequest, inRecord, inStatus2] = await listener;
Assert.equal(inRequest, request, "correct request was used");
Assert.ok(
!Components.isSuccessCode(inStatus2),
`${inStatus2} should be an error code`
);
});

Просмотреть файл

@ -0,0 +1,8 @@
/* import-globals-from ../unit/head_trr.js */
var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
var { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
load("../unit/head_trr.js");

Просмотреть файл

@ -67,5 +67,15 @@ function run_test() {
"network.trr.uri",
"https://foo.example.com:" + h2Port + "/httpssvc"
);
do_await_remote_message("mode3-port").then(port => {
prefs.setIntPref("network.trr.mode", 3);
prefs.setCharPref(
"network.trr.uri",
`https://foo.example.com:${port}/dns-query`
);
do_send_remote_message("mode3-port-done");
});
run_test_in_child("../unit/test_trr_httpssvc.js");
}

Просмотреть файл

@ -1,5 +1,5 @@
[DEFAULT]
head = head_channels_clone.js
head = head_channels_clone.js head_trr_clone.js
skip-if = toolkit == 'android'
support-files =
child_channel_id.js
@ -38,6 +38,7 @@ support-files =
!/netwerk/test/unit/test_trackingProtection_annotateChannels.js
!/netwerk/test/unit/test_xmlhttprequest.js
!/netwerk/test/unit/head_channels.js
!/netwerk/test/unit/head_trr.js
!/netwerk/test/unit/head_cache2.js
!/netwerk/test/unit/data/image.png
!/netwerk/test/unit/data/system_root.lnk