diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java b/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java index 41abba7837..75c7e6c7a1 100644 --- a/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java +++ b/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java @@ -101,4 +101,8 @@ public class MdnsConfigs { public static boolean allowNetworkInterfaceIndexPropagation() { return true; } + + public static boolean allowMultipleSrvRecordsPerHost() { + return true; + } } \ No newline at end of file diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java index 57b241e8ca..e73de55c19 100644 --- a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java +++ b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java @@ -25,6 +25,7 @@ import com.android.server.connectivity.mdns.util.MdnsLogger; import java.io.EOFException; import java.io.IOException; import java.net.DatagramPacket; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -37,6 +38,8 @@ public class MdnsResponseDecoder { public static final int SUCCESS = 0; private static final String TAG = "MdnsResponseDecoder"; private static final MdnsLogger LOGGER = new MdnsLogger(TAG); + private final boolean allowMultipleSrvRecordsPerHost = + MdnsConfigs.allowMultipleSrvRecordsPerHost(); private final String[] serviceType; private final Clock clock; @@ -281,13 +284,18 @@ public class MdnsResponseDecoder { for (MdnsRecord record : records) { if (record instanceof MdnsInetAddressRecord) { MdnsInetAddressRecord inetRecord = (MdnsInetAddressRecord) record; - MdnsResponse response = findResponseWithHostName(responses, inetRecord.getName()); - if (inetRecord.getInet4Address() != null && response != null) { - response.setInet4AddressRecord(inetRecord); - response.setInterfaceIndex(interfaceIndex); - } else if (inetRecord.getInet6Address() != null && response != null) { - response.setInet6AddressRecord(inetRecord); - response.setInterfaceIndex(interfaceIndex); + if (allowMultipleSrvRecordsPerHost) { + List matchingResponses = + findResponsesWithHostName(responses, inetRecord.getName()); + for (MdnsResponse response : matchingResponses) { + assignInetRecord(response, inetRecord, interfaceIndex); + } + } else { + MdnsResponse response = + findResponseWithHostName(responses, inetRecord.getName()); + if (response != null) { + assignInetRecord(response, inetRecord, interfaceIndex); + } } } } @@ -295,6 +303,39 @@ public class MdnsResponseDecoder { return SUCCESS; } + private static void assignInetRecord( + MdnsResponse response, MdnsInetAddressRecord inetRecord, int interfaceIndex) { + if (inetRecord.getInet4Address() != null) { + response.setInet4AddressRecord(inetRecord); + response.setInterfaceIndex(interfaceIndex); + } else if (inetRecord.getInet6Address() != null) { + response.setInet6AddressRecord(inetRecord); + response.setInterfaceIndex(interfaceIndex); + } + } + + private static List findResponsesWithHostName( + @Nullable List responses, String[] hostName) { + if (responses == null || responses.isEmpty()) { + return List.of(); + } + + List result = null; + for (MdnsResponse response : responses) { + MdnsServiceRecord serviceRecord = response.getServiceRecord(); + if (serviceRecord == null) { + continue; + } + if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) { + if (result == null) { + result = new ArrayList<>(/* initialCapacity= */ responses.size()); + } + result.add(response); + } + } + return result == null ? List.of() : result; + } + public static class Clock { public long elapsedRealtime() { return SystemClock.elapsedRealtime(); diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java index 8d0ace5b4b..02e00c2244 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java @@ -26,11 +26,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import android.net.InetAddresses; + import com.android.net.module.util.HexDump; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -106,9 +109,49 @@ public class MdnsResponseDecoderTests { + "63616C0000018001000000780004C0A8010A000001800100000078" + "0004C0A8010A00000000000000"); + // Expected to contain two SRV records which point to the same hostname. + private static final byte[] matterDuplicateHostname = HexDump.hexStringToByteArray( + "00008000000000080000000A095F7365727669636573075F646E732D73" + + "64045F756470056C6F63616C00000C000100000078000F075F6D61" + + "74746572045F746370C023C00C000C000100000078001A125F4943" + + "324639453337374632454139463430045F737562C034C034000C00" + + "0100000078002421433246394533373746324541394634302D3030" + + "3030303030304534443041334641C034C04F000C00010000007800" + + "02C075C00C000C0001000000780002C034C00C000C000100000078" + + "0015125F4941413035363731333439334135343144C062C034000C" + + "000100000078002421414130353637313334393341353431442D30" + + "303030303030304331324446303344C034C0C1000C000100000078" + + "0002C0E2C075002100010000007800150000000015A40C33433631" + + "3035304338394638C023C07500100001000011940015084352493D" + + "35303030074352413D33303003543D31C126001C00010000007800" + + "10FE800000000000003E6105FFFE0C89F8C126001C000100000078" + + "00102605A601A84657003E6105FFFE0C89F8C12600010001000000" + + "780004C0A8018AC0E2002100010000007800080000000015A4C126" + + "C0E200100001000011940015084352493D35303030074352413D33" + + "303003543D31C126001C0001000000780010FE800000000000003E" + + "6105FFFE0C89F8C126001C00010000007800102605A601A8465700" + + "3E6105FFFE0C89F8C12600010001000000780004C0A8018A313035" + + "304338394638C02300010001000000780004C0A8018AC0A0001000" + + "0100001194003A0E56503D36353532312B3332373639084352493D" + + "35303030074352413D33303003543D3106443D3236353704434D3D" + + "320550483D33360350493D21433246394533373746324541394634" + + "302D30303030303030304534443041334641C0F700210001000000" + + "7800150000000015A40C334336313035304338394638C023214332" + + "46394533373746324541394634302D303030303030303045344430" + + "41334641C0F700100001000011940015084352493D353030300743" + + "52413D33303003543D310C334336313035304338394638C023001C" + + "0001000000780010FE800000000000003E6105FFFE0C89F80C3343" + + "36313035304338394638C023001C00010000007800102605A601A8" + + "4657003E6105FFFE0C89F80C334336313035304338394638C02300" + + "010001000000780004C0A8018A0000000000000000000000000000" + + "000000"); + private static final String CAST_SERVICE_NAME = "_googlecast"; private static final String[] CAST_SERVICE_TYPE = new String[] {CAST_SERVICE_NAME, "_tcp", "local"}; + private static final String MATTER_SERVICE_NAME = "_matter"; + private static final String[] MATTER_SERVICE_TYPE = + new String[] {MATTER_SERVICE_NAME, "_tcp", "local"}; private final List responses = new LinkedList<>(); @@ -249,4 +292,62 @@ public class MdnsResponseDecoderTests { assertEquals(responses.size(), 1); assertEquals(responses.get(0).getInterfaceIndex(), 10); } + + @Test + public void decode_singleHostname_multipleSrvRecords_flagEnabled_multipleCompleteResponses() { + //MdnsScannerConfigsFlagsImpl.allowMultipleSrvRecordsPerHost.override(true); + MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, MATTER_SERVICE_TYPE); + assertNotNull(matterDuplicateHostname); + + DatagramPacket packet = + new DatagramPacket(matterDuplicateHostname, matterDuplicateHostname.length); + + packet.setSocketAddress( + new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT)); + + responses.clear(); + int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0); + assertEquals(MdnsResponseDecoder.SUCCESS, errorCode); + + // This should emit two records: + assertEquals(2, responses.size()); + + MdnsResponse response1 = responses.get(0); + MdnsResponse response2 = responses.get(0); + + // Both of which are complete: + assertTrue(response1.isComplete()); + assertTrue(response2.isComplete()); + + // And should both have the same IPv6 address: + assertEquals(InetAddresses.parseNumericAddress("2605:a601:a846:5700:3e61:5ff:fe0c:89f8"), + response1.getInet6AddressRecord().getInet6Address()); + assertEquals(InetAddresses.parseNumericAddress("2605:a601:a846:5700:3e61:5ff:fe0c:89f8"), + response2.getInet6AddressRecord().getInet6Address()); + } + + @Test + @Ignore("MdnsConfigs is not configurable currently.") + public void decode_singleHostname_multipleSrvRecords_flagDisabled_singleCompleteResponse() { + //MdnsScannerConfigsFlagsImpl.allowMultipleSrvRecordsPerHost.override(false); + MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, MATTER_SERVICE_TYPE); + assertNotNull(matterDuplicateHostname); + + DatagramPacket packet = + new DatagramPacket(matterDuplicateHostname, matterDuplicateHostname.length); + + packet.setSocketAddress( + new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT)); + + responses.clear(); + int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0); + assertEquals(MdnsResponseDecoder.SUCCESS, errorCode); + + // This should emit only two records: + assertEquals(2, responses.size()); + + // But only the first is complete: + assertTrue(responses.get(0).isComplete()); + assertFalse(responses.get(1).isComplete()); + } } \ No newline at end of file