• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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