1 /*
2  * Copyright (C) 2024 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 android.net.util
18 
19 import android.content.Context
20 import android.net.TetheringManager
21 import android.system.Os
22 import com.android.dx.mockito.inline.extended.ExtendedMockito
23 import com.android.net.module.util.HexDump
24 import com.android.testutils.DevSdkIgnoreRule
25 import com.android.testutils.DevSdkIgnoreRunner
26 import java.io.FileDescriptor
27 import java.net.NetworkInterface
28 import kotlin.test.assertEquals
29 import kotlin.test.assertFailsWith
30 import org.junit.After
31 import org.junit.Before
32 import org.junit.Rule
33 import org.junit.Test
34 import org.junit.runner.RunWith
35 import org.mockito.ArgumentCaptor
36 import org.mockito.ArgumentMatchers.any
37 import org.mockito.ArgumentMatchers.eq
38 import org.mockito.Mock
39 import org.mockito.Mockito.doAnswer
40 import org.mockito.Mockito.doReturn
41 import org.mockito.Mockito.framework
42 import org.mockito.Mockito.`when`
43 import org.mockito.MockitoSession
44 import org.mockito.quality.Strictness
45 
46 @RunWith(DevSdkIgnoreRunner::class)
47 class RawSocketUtilsTest {
48     @get:Rule
49     val ignoreRule = DevSdkIgnoreRule()
50     companion object {
51         private const val TEST_IFINDEX = 123
52         private const val TEST_IFACENAME = "wlan0"
53         private const val TEST_SRC_MAC = "FFFFFFFFFFFF"
54         private const val TEST_DST_MAC = "1234567890AB"
55         private const val TEST_INVALID_PACKET_IN_HEX = "DEADBEEF"
56         private const val TEST_PACKET_TYPE_IN_HEX = "88A4"
57         private const val TEST_VALID_PACKET_IN_HEX =
58                 TEST_DST_MAC + TEST_SRC_MAC + TEST_PACKET_TYPE_IN_HEX
59     }
60     @Mock
61     private lateinit var mockContext: Context
62     @Mock
63     private lateinit var mockTetheringManager: TetheringManager
64     @Mock
65     private lateinit var mockNetworkInterface: NetworkInterface
66 
67     // For mocking static methods.
68     private lateinit var mockitoSession: MockitoSession
69 
70     @Before
setupnull71     fun setup() {
72         mockitoSession = ExtendedMockito.mockitoSession()
73                 .mockStatic(Os::class.java)
74                 .mockStatic(NetworkInterface::class.java)
75                 .mockStatic(SocketUtils::class.java)
76                 .initMocks(this)
77                 .strictness(Strictness.LENIENT)
78                 .startMocking()
79         doReturn(mockTetheringManager).`when`(mockContext)
80                 .getSystemService(eq(TetheringManager::class.java))
81         `when`(NetworkInterface.getByName(any())).thenReturn(mockNetworkInterface)
82         doReturn(TEST_IFINDEX).`when`(mockNetworkInterface).index
83     }
84 
85     @After
teardownnull86     fun teardown() {
87         mockitoSession.finishMocking()
88         // Clear mocks to prevent from stubs holding instances and cause memory leaks.
89         framework().clearInlineMocks()
90     }
91 
92     @Test
sendRawPacketDownStream_invalidTetheredInterfacenull93     fun sendRawPacketDownStream_invalidTetheredInterface() {
94         doAnswer {
95             val callback = it.arguments[1] as TetheringManager.TetheringEventCallback
96             callback.onTetheredInterfacesChanged(listOf("eth0"))
97         }.`when`(mockTetheringManager).registerTetheringEventCallback(any(), any())
98         assertFailsWith<SecurityException> {
99             RawSocketUtils.sendRawPacketDownStream(
100                 mockContext,
101                 TEST_IFACENAME,
102                 TEST_INVALID_PACKET_IN_HEX
103             )
104         }
105     }
106 
107     @Test
sendRawPacketDownStream_invalidPacketnull108     fun sendRawPacketDownStream_invalidPacket() {
109         doAnswer {
110             val callback = it.arguments[1] as TetheringManager.TetheringEventCallback
111             callback.onTetheredInterfacesChanged(listOf(TEST_IFACENAME))
112         }.`when`(mockTetheringManager).registerTetheringEventCallback(any(), any())
113 
114         assertFailsWith<ArrayIndexOutOfBoundsException> {
115             RawSocketUtils.sendRawPacketDownStream(
116                     mockContext,
117                     TEST_IFACENAME,
118                     TEST_INVALID_PACKET_IN_HEX
119             )
120         }
121     }
122 
123     @Test
sendRawPacketDownStream_validPacketnull124     fun sendRawPacketDownStream_validPacket() {
125         doAnswer {
126             val callback = it.arguments[1] as TetheringManager.TetheringEventCallback
127             callback.onTetheredInterfacesChanged(listOf(TEST_IFACENAME))
128         }.`when`(mockTetheringManager).registerTetheringEventCallback(any(), any())
129 
130         RawSocketUtils.sendRawPacketDownStream(
131             mockContext,
132             TEST_IFACENAME,
133             TEST_VALID_PACKET_IN_HEX
134         )
135 
136         // Verify interactions with mocked static methods.
137         val fileDescriptorCaptor = ArgumentCaptor.forClass(FileDescriptor::class.java)
138         val packetDataCaptor = ArgumentCaptor.forClass(ByteArray::class.java)
139         val packetDataLengthCaptor = ArgumentCaptor.forClass(Int::class.java)
140         ExtendedMockito.verify {
141             Os.sendto(
142                 fileDescriptorCaptor.capture(),
143                 packetDataCaptor.capture(),
144                 eq(0),
145                 packetDataLengthCaptor.capture(),
146                 eq(0),
147                 any()
148             )
149         }
150         assertEquals(TEST_VALID_PACKET_IN_HEX, HexDump.toHexString(packetDataCaptor.value))
151         assertEquals(TEST_VALID_PACKET_IN_HEX.length / 2, packetDataLengthCaptor.value)
152         // TODO: Verify ifindex and packetType once the members of PacketSocketAddress
153         //  can be accessed.
154         ExtendedMockito.verify { SocketUtils.closeSocket(eq(fileDescriptorCaptor.value)) }
155     }
156 }
157