1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity.mdns; 18 19 import static com.android.server.connectivity.mdns.MdnsSocketProvider.SocketCallback; 20 import static com.android.server.connectivity.mdns.MulticastPacketReader.PacketHandler; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.Mockito.doReturn; 26 import static org.mockito.Mockito.eq; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.timeout; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.verifyNoMoreInteractions; 33 34 import android.net.InetAddresses; 35 import android.net.Network; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.HandlerThread; 39 40 import com.android.net.module.util.HexDump; 41 import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback; 42 import com.android.testutils.DevSdkIgnoreRule; 43 import com.android.testutils.DevSdkIgnoreRunner; 44 import com.android.testutils.HandlerUtils; 45 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.mockito.ArgumentCaptor; 50 import org.mockito.Mock; 51 import org.mockito.MockitoAnnotations; 52 53 import java.io.IOException; 54 import java.lang.reflect.Constructor; 55 import java.net.DatagramPacket; 56 import java.net.NetworkInterface; 57 import java.net.SocketException; 58 import java.util.List; 59 60 @RunWith(DevSdkIgnoreRunner.class) 61 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) 62 public class MdnsMultinetworkSocketClientTest { 63 private static final byte[] BUFFER = new byte[10]; 64 private static final long DEFAULT_TIMEOUT = 2000L; 65 @Mock private Network mNetwork; 66 @Mock private MdnsSocketProvider mProvider; 67 @Mock private MdnsInterfaceSocket mSocket; 68 @Mock private MdnsServiceBrowserListener mListener; 69 @Mock private MdnsSocketClientBase.Callback mCallback; 70 @Mock private SocketCreationCallback mSocketCreationCallback; 71 private MdnsMultinetworkSocketClient mSocketClient; 72 private Handler mHandler; 73 74 @Before setUp()75 public void setUp() throws SocketException { 76 MockitoAnnotations.initMocks(this); 77 final HandlerThread thread = new HandlerThread("MdnsMultinetworkSocketClientTest"); 78 thread.start(); 79 mHandler = new Handler(thread.getLooper()); 80 mSocketClient = new MdnsMultinetworkSocketClient(thread.getLooper(), mProvider); 81 mHandler.post(() -> mSocketClient.setCallback(mCallback)); 82 } 83 expectSocketCallback()84 private SocketCallback expectSocketCallback() { 85 return expectSocketCallback(mListener, mNetwork); 86 } 87 expectSocketCallback(MdnsServiceBrowserListener listener, Network requestedNetwork)88 private SocketCallback expectSocketCallback(MdnsServiceBrowserListener listener, 89 Network requestedNetwork) { 90 final ArgumentCaptor<SocketCallback> callbackCaptor = 91 ArgumentCaptor.forClass(SocketCallback.class); 92 mHandler.post(() -> mSocketClient.notifyNetworkRequested( 93 listener, requestedNetwork, mSocketCreationCallback)); 94 verify(mProvider, timeout(DEFAULT_TIMEOUT)) 95 .requestSocket(eq(requestedNetwork), callbackCaptor.capture()); 96 return callbackCaptor.getValue(); 97 } 98 createEmptyNetworkInterface()99 private NetworkInterface createEmptyNetworkInterface() { 100 try { 101 Constructor<NetworkInterface> constructor = 102 NetworkInterface.class.getDeclaredConstructor(); 103 constructor.setAccessible(true); 104 return constructor.newInstance(); 105 } catch (Exception e) { 106 throw new RuntimeException(e); 107 } 108 } 109 110 @Test testSendPacket()111 public void testSendPacket() throws IOException { 112 final SocketCallback callback = expectSocketCallback(); 113 final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length, 114 InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */); 115 final DatagramPacket ipv6Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length, 116 InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */); 117 118 final MdnsInterfaceSocket tetherIfaceSock1 = mock(MdnsInterfaceSocket.class); 119 final MdnsInterfaceSocket tetherIfaceSock2 = mock(MdnsInterfaceSocket.class); 120 for (MdnsInterfaceSocket socket : List.of(mSocket, tetherIfaceSock1, tetherIfaceSock2)) { 121 doReturn(true).when(socket).hasJoinedIpv4(); 122 doReturn(true).when(socket).hasJoinedIpv6(); 123 doReturn(createEmptyNetworkInterface()).when(socket).getInterface(); 124 } 125 126 // Notify socket created 127 callback.onSocketCreated(mNetwork, mSocket, List.of()); 128 verify(mSocketCreationCallback).onSocketCreated(mNetwork); 129 callback.onSocketCreated(null, tetherIfaceSock1, List.of()); 130 verify(mSocketCreationCallback).onSocketCreated(null); 131 callback.onSocketCreated(null, tetherIfaceSock2, List.of()); 132 verify(mSocketCreationCallback, times(2)).onSocketCreated(null); 133 134 // Send packet to IPv4 with target network and verify sending has been called. 135 mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork); 136 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 137 verify(mSocket).send(ipv4Packet); 138 verify(tetherIfaceSock1, never()).send(any()); 139 verify(tetherIfaceSock2, never()).send(any()); 140 141 // Send packet to IPv6 without target network and verify sending has been called. 142 mSocketClient.sendMulticastPacket(ipv6Packet, null); 143 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 144 verify(mSocket, never()).send(ipv6Packet); 145 verify(tetherIfaceSock1).send(ipv6Packet); 146 verify(tetherIfaceSock2).send(ipv6Packet); 147 } 148 149 @Test testReceivePacket()150 public void testReceivePacket() { 151 final SocketCallback callback = expectSocketCallback(); 152 final byte[] data = HexDump.hexStringToByteArray( 153 // scapy.raw(scapy.dns_compress( 154 // scapy.DNS(rd=0, qr=1, aa=1, qd = None, 155 // an = 156 // scapy.DNSRR(type='PTR', rrname='_testtype._tcp.local', 157 // rdata='testservice._testtype._tcp.local', rclass='IN', ttl=4500) / 158 // scapy.DNSRRSRV(rrname='testservice._testtype._tcp.local', rclass=0x8001, 159 // port=31234, target='Android.local', ttl=120)) 160 // )).hex().upper() 161 "000084000000000200000000095F7465737474797065045F746370056C6F63616C00000C0001000011" 162 + "94000E0B7465737473657276696365C00CC02C00218001000000780010000000007A0207" 163 + "416E64726F6964C01B"); 164 165 doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); 166 // Notify socket created 167 callback.onSocketCreated(mNetwork, mSocket, List.of()); 168 verify(mSocketCreationCallback).onSocketCreated(mNetwork); 169 170 final ArgumentCaptor<PacketHandler> handlerCaptor = 171 ArgumentCaptor.forClass(PacketHandler.class); 172 verify(mSocket).addPacketHandler(handlerCaptor.capture()); 173 174 // Send the data and verify the received records. 175 final PacketHandler handler = handlerCaptor.getValue(); 176 handler.handlePacket(data, data.length, null /* src */); 177 final ArgumentCaptor<MdnsPacket> responseCaptor = 178 ArgumentCaptor.forClass(MdnsPacket.class); 179 verify(mCallback).onResponseReceived(responseCaptor.capture(), anyInt(), any()); 180 final MdnsPacket response = responseCaptor.getValue(); 181 assertEquals(0, response.questions.size()); 182 assertEquals(0, response.additionalRecords.size()); 183 assertEquals(0, response.authorityRecords.size()); 184 185 final String[] serviceName = "testservice._testtype._tcp.local".split("\\."); 186 assertEquals(List.of( 187 new MdnsPointerRecord("_testtype._tcp.local".split("\\."), 188 0L /* receiptTimeMillis */, false /* cacheFlush */, 4500000 /* ttlMillis */, 189 serviceName), 190 new MdnsServiceRecord(serviceName, 0L /* receiptTimeMillis */, 191 false /* cacheFlush */, 4500000 /* ttlMillis */, 0 /* servicePriority */, 192 0 /* serviceWeight */, 31234 /* servicePort */, 193 new String[] { "Android", "local" } /* serviceHost */) 194 ), response.answers); 195 } 196 197 @Test testSocketRemovedAfterNetworkUnrequested()198 public void testSocketRemovedAfterNetworkUnrequested() throws IOException { 199 // Request sockets on all networks 200 final SocketCallback callback = expectSocketCallback(mListener, null); 201 final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length, 202 InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */); 203 204 // Notify 3 socket created, including 2 tethered interfaces (null network) 205 final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class); 206 final MdnsInterfaceSocket socket3 = mock(MdnsInterfaceSocket.class); 207 doReturn(true).when(mSocket).hasJoinedIpv4(); 208 doReturn(true).when(mSocket).hasJoinedIpv6(); 209 doReturn(true).when(socket2).hasJoinedIpv4(); 210 doReturn(true).when(socket2).hasJoinedIpv6(); 211 doReturn(true).when(socket3).hasJoinedIpv4(); 212 doReturn(true).when(socket3).hasJoinedIpv6(); 213 doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); 214 doReturn(createEmptyNetworkInterface()).when(socket2).getInterface(); 215 doReturn(createEmptyNetworkInterface()).when(socket3).getInterface(); 216 217 callback.onSocketCreated(mNetwork, mSocket, List.of()); 218 callback.onSocketCreated(null, socket2, List.of()); 219 callback.onSocketCreated(null, socket3, List.of()); 220 verify(mSocketCreationCallback).onSocketCreated(mNetwork); 221 verify(mSocketCreationCallback, times(2)).onSocketCreated(null); 222 223 // Send IPv4 packet on the non-null Network and verify sending has been called. 224 mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork); 225 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 226 verify(mSocket).send(ipv4Packet); 227 verify(socket2, never()).send(any()); 228 verify(socket3, never()).send(any()); 229 230 // Request another socket with null network, get the same interfaces 231 final SocketCreationCallback socketCreationCb2 = mock(SocketCreationCallback.class); 232 final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class); 233 234 // requestSocket is called a second time 235 final ArgumentCaptor<SocketCallback> callback2Captor = 236 ArgumentCaptor.forClass(SocketCallback.class); 237 mHandler.post(() -> mSocketClient.notifyNetworkRequested( 238 listener2, null, socketCreationCb2)); 239 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 240 verify(mProvider, times(2)).requestSocket(eq(null), callback2Captor.capture()); 241 final SocketCallback callback2 = callback2Captor.getAllValues().get(1); 242 243 // Notify socket created for all networks. 244 callback2.onSocketCreated(mNetwork, mSocket, List.of()); 245 callback2.onSocketCreated(null, socket2, List.of()); 246 callback2.onSocketCreated(null, socket3, List.of()); 247 verify(socketCreationCb2).onSocketCreated(mNetwork); 248 verify(socketCreationCb2, times(2)).onSocketCreated(null); 249 250 // Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets. 251 mSocketClient.sendMulticastPacket(ipv4Packet, null); 252 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 253 // ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on 254 // mNetwork 255 verify(mSocket, times(1)).send(ipv4Packet); 256 verify(socket2).send(ipv4Packet); 257 verify(socket3).send(ipv4Packet); 258 259 // Unregister the second request 260 mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(listener2)); 261 verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2); 262 263 // Send IPv4 packet again and verify it's still sent a second time 264 mSocketClient.sendMulticastPacket(ipv4Packet, null); 265 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 266 verify(socket2, times(2)).send(ipv4Packet); 267 verify(socket3, times(2)).send(ipv4Packet); 268 269 // Unrequest remaining sockets 270 mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener)); 271 verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback); 272 273 // Send IPv4 packet and verify no more sending. 274 mSocketClient.sendMulticastPacket(ipv4Packet, null); 275 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 276 verify(mSocket, times(1)).send(ipv4Packet); 277 verify(socket2, times(2)).send(ipv4Packet); 278 verify(socket3, times(2)).send(ipv4Packet); 279 } 280 281 @Test testNotifyNetworkUnrequested_SocketsOnNullNetwork()282 public void testNotifyNetworkUnrequested_SocketsOnNullNetwork() { 283 final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class); 284 final SocketCallback callback = expectSocketCallback( 285 mListener, null /* requestedNetwork */); 286 doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); 287 doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface(); 288 289 callback.onSocketCreated(null /* network */, mSocket, List.of()); 290 verify(mSocketCreationCallback).onSocketCreated(null); 291 callback.onSocketCreated(null /* network */, otherSocket, List.of()); 292 verify(mSocketCreationCallback, times(2)).onSocketCreated(null); 293 294 verify(mSocketCreationCallback, never()).onAllSocketsDestroyed(null /* network */); 295 mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener)); 296 HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); 297 298 verify(mProvider).unrequestSocket(callback); 299 verify(mSocketCreationCallback).onAllSocketsDestroyed(null /* network */); 300 } 301 302 @Test testSocketCreatedAndDestroyed_NullNetwork()303 public void testSocketCreatedAndDestroyed_NullNetwork() throws IOException { 304 final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class); 305 final SocketCallback callback = expectSocketCallback(mListener, null /* network */); 306 doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); 307 doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface(); 308 309 callback.onSocketCreated(null /* network */, mSocket, List.of()); 310 verify(mSocketCreationCallback).onSocketCreated(null); 311 callback.onSocketCreated(null /* network */, otherSocket, List.of()); 312 verify(mSocketCreationCallback, times(2)).onSocketCreated(null); 313 314 // Notify socket destroyed 315 callback.onInterfaceDestroyed(null /* network */, mSocket); 316 verifyNoMoreInteractions(mSocketCreationCallback); 317 callback.onInterfaceDestroyed(null /* network */, otherSocket); 318 verify(mSocketCreationCallback).onAllSocketsDestroyed(null /* network */); 319 } 320 } 321