• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.google.android.iwlan.epdg;
18 
19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
20 
21 import static com.google.android.iwlan.epdg.NaptrDnsResolver.*;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.ArgumentMatchers.anyInt;
27 import static org.mockito.Mockito.doAnswer;
28 import static org.mockito.Mockito.lenient;
29 
30 import android.net.DnsResolver;
31 import android.net.Network;
32 import android.support.annotation.NonNull;
33 import android.support.annotation.Nullable;
34 import android.util.Log;
35 
36 import com.google.android.iwlan.epdg.NaptrDnsResolver.NaptrTarget;
37 
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.mockito.ArgumentMatchers;
42 import org.mockito.Mock;
43 import org.mockito.MockitoAnnotations;
44 import org.mockito.MockitoSession;
45 
46 import java.net.UnknownHostException;
47 import java.util.List;
48 import java.util.concurrent.CompletableFuture;
49 import java.util.concurrent.CompletionException;
50 import java.util.concurrent.ExecutionException;
51 import java.util.concurrent.Executor;
52 import java.util.concurrent.Executors;
53 
54 public class NaptrDnsResolverTest {
55     private static final String TAG = "NaptrDnsResolverTest";
56 
57     private static final String TEST_DOMAIN_NAME = "columbia.edu";
58     private static final String TEST_DOMAIN_NAME_U_RECORD = "columbia.urecord.edu";
59 
60     // SRV record response to TEST_DOMAIN_NAME. Reproduced with "dig columbia.edu -tNAPTR".
61     private static final byte[] TEST_DOMAIN_NAME_NAPTR_RESPONSE = {
62         -15, 85, -127, -128, 0, 1, 0, 1, 0, 0, 0, 0, 8, 99, 111, 108, 117, 109, 98, 105, 97, 3, 101,
63         100, 117, 0, 0, 35, 0, 1, -64, 12, 0, 35, 0, 1, 0, 0, 11, -88, 0, 39, 0, 1, 0, 0, 1, 115, 7,
64         83, 73, 80, 43, 68, 50, 85, 0, 4, 95, 115, 105, 112, 4, 95, 117, 100, 112, 8, 99, 111, 108,
65         117, 109, 98, 105, 97, 3, 101, 100, 117, 0
66     };
67 
68     // Same as TEST_DOMAIN_NAME_NAPTR_RESPONSE, but with flag field set to 'u' instead of 's'.
69     private static final byte[] TEST_DOMAIN_NAME_U_RECORD_NAPTR_RESPONSE = {
70         -15, 85, -127, -128, 0, 1, 0, 1, 0, 0, 0, 0, 8, 99, 111, 108, 117, 109, 98, 105, 97, 3, 101,
71         100, 117, 0, 0, 35, 0, 1, -64, 12, 0, 35, 0, 1, 0, 0, 11, -88, 0, 39, 0, 1, 0, 0, 1, 117, 7,
72         83, 73, 80, 43, 68, 50, 85, 0, 4, 95, 115, 105, 112, 4, 95, 117, 100, 112, 8, 99, 111, 108,
73         117, 109, 98, 105, 97, 3, 101, 100, 117, 0
74     };
75 
76     @Mock private Network mMockNetwork;
77     @Mock private DnsResolver mMockDnsResolver;
78 
79     MockitoSession mStaticMockSession;
80     CompletableFuture<List<NaptrTarget>> mNaptrDnsResult;
81     DnsResolver.Callback<List<NaptrTarget>> mNaptrDnsCb;
82 
NaptrDnsResolverTest()83     public NaptrDnsResolverTest() {
84         mNaptrDnsResult = new CompletableFuture<>();
85         mNaptrDnsCb =
86                 new DnsResolver.Callback<List<NaptrTarget>>() {
87                     @Override
88                     public void onAnswer(@NonNull final List<NaptrTarget> answer, final int rcode) {
89                         if (rcode == 0 && answer.size() != 0) {
90                             mNaptrDnsResult.complete(answer);
91                         } else {
92                             mNaptrDnsResult.completeExceptionally(new UnknownHostException());
93                         }
94                     }
95 
96                     @Override
97                     public void onError(@Nullable final DnsResolver.DnsException error) {
98                         mNaptrDnsResult.completeExceptionally(error);
99                     }
100                 };
101     }
102 
103     @Before
setUp()104     public void setUp() throws Exception {
105         MockitoAnnotations.initMocks(this);
106         mStaticMockSession = mockitoSession().mockStatic(DnsResolver.class).startMocking();
107 
108         // lenient() here is used to mock the static method.
109         lenient().when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver);
110     }
111 
112     @After
cleanUp()113     public void cleanUp() throws Exception {
114         mStaticMockSession.finishMocking();
115     }
116 
117     // Demonstrates that NAPTR responses with flag field 'S' and 'A' will be parsed as expected.
118     @Test
testValidHostNameGivesNaptrResponse()119     public void testValidHostNameGivesNaptrResponse()
120             throws ExecutionException, InterruptedException {
121         doAnswer(
122                         invocation -> {
123                             final Executor executor = invocation.getArgument(5);
124                             final DnsResolver.Callback<byte[]> callback = invocation.getArgument(7);
125                             executor.execute(
126                                     () -> callback.onAnswer(TEST_DOMAIN_NAME_NAPTR_RESPONSE, 0));
127                             return null;
128                         })
129                 .when(mMockDnsResolver)
130                 .rawQuery(
131                         any(),
132                         ArgumentMatchers.eq(TEST_DOMAIN_NAME),
133                         ArgumentMatchers.eq(DnsResolver.CLASS_IN),
134                         ArgumentMatchers.eq(QUERY_TYPE_NAPTR),
135                         anyInt(),
136                         any(),
137                         any(),
138                         any());
139 
140         NaptrDnsResolver.query(
141                 mMockNetwork,
142                 TEST_DOMAIN_NAME,
143                 Executors.newSingleThreadExecutor(),
144                 null,
145                 mNaptrDnsCb);
146         List<NaptrTarget> records = mNaptrDnsResult.join();
147         assertEquals(records.size(), 1);
148 
149         NaptrTarget record = records.get(0);
150         assertEquals(record.mName, "_sip._udp.columbia.edu");
151         // SRV record type.
152         assertEquals(record.mType, TYPE_SRV);
153     }
154 
155     // Demonstrates that NAPTR responses with flag field 'U' and 'P' are unexpected and with throw
156     // a DnsException.
157     @Test
testValidHostNameGivesParsingErrorForUnexpectedResponse()158     public void testValidHostNameGivesParsingErrorForUnexpectedResponse()
159             throws ExecutionException, InterruptedException {
160         doAnswer(
161                         invocation -> {
162                             final Executor executor = invocation.getArgument(5);
163                             final DnsResolver.Callback<byte[]> callback = invocation.getArgument(7);
164                             executor.execute(
165                                     () ->
166                                             callback.onAnswer(
167                                                     TEST_DOMAIN_NAME_U_RECORD_NAPTR_RESPONSE, 0));
168                             return null;
169                         })
170                 .when(mMockDnsResolver)
171                 .rawQuery(
172                         any(),
173                         ArgumentMatchers.eq(TEST_DOMAIN_NAME_U_RECORD),
174                         ArgumentMatchers.eq(DnsResolver.CLASS_IN),
175                         ArgumentMatchers.eq(QUERY_TYPE_NAPTR),
176                         anyInt(),
177                         any(),
178                         any(),
179                         any());
180 
181         NaptrDnsResolver.query(
182                 mMockNetwork,
183                 TEST_DOMAIN_NAME_U_RECORD,
184                 Executors.newSingleThreadExecutor(),
185                 null,
186                 mNaptrDnsCb);
187 
188         DnsResolver.DnsException exception = null;
189         try {
190             mNaptrDnsResult.join();
191         } catch (CompletionException e) {
192             exception = (DnsResolver.DnsException) e.getCause();
193             Log.d(TAG, e.getMessage() + e.getCause());
194         }
195         assertNotNull("Exception wasn't thrown!", exception);
196         assertEquals(exception.code, DnsResolver.ERROR_PARSE);
197     }
198 }
199