1 /* 2 * Copyright (C) 2023 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.tv.mdnsoffloadmanager; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertThrows; 21 22 import androidx.test.filters.SmallTest; 23 import org.junit.Test; 24 25 import java.util.Arrays; 26 import java.util.List; 27 28 import device.google.atv.mdns_offload.IMdnsOffload.MdnsProtocolData.MatchCriteria; 29 30 @SmallTest 31 public class MdnsPacketParserTest { 32 33 @Test testExtractFullNameFromSingleLabel()34 public void testExtractFullNameFromSingleLabel() { 35 byte[] array = new byte[]{'x', 'x', 'x', 3, 'a', 't', 'v', 0}; 36 String fullName = MdnsPacketParser.extractFullName(array, 3); 37 assertEquals("atv.", fullName); 38 } 39 40 @Test testExtractFullNameFromPointer()41 public void testExtractFullNameFromPointer() { 42 byte[] array = new byte[]{3, 'i', 's', 'o', 0, (byte) 0xC0, 0x00}; 43 String fullName = MdnsPacketParser.extractFullName(array, 5); 44 assertEquals("iso.", fullName); 45 } 46 47 @Test testExtractFullNameFromMultiLabel()48 public void testExtractFullNameFromMultiLabel() { 49 byte[] array = new byte[]{'x', 'x', 'x', 3, 'a', 't', 'v', 3, 'g', 't', 'v', 0}; 50 String fullName = MdnsPacketParser.extractFullName(array, 3); 51 assertEquals("atv.gtv.", fullName); 52 } 53 54 @Test testExtractFullNameFromLabelPointer()55 public void testExtractFullNameFromLabelPointer() { 56 byte[] array = new byte[]{3, 'i', 's', 'o', 0,// 57 3, 'a', 't', 'v', 3, 'g', 't', 'v', (byte) 0xC0, 0x00}; 58 String fullName = MdnsPacketParser.extractFullName(array, 9); 59 assertEquals("gtv.iso.", fullName); 60 } 61 62 @Test testExtractFullNameFromLabelDualPointerLongOffset()63 public void testExtractFullNameFromLabelDualPointerLongOffset() { 64 byte[] array = new byte[]{3, 'i', 's', 'o', 0,// 65 3, 'a', 't', 'v', 3, 'g', 't', 'v', (byte) 0xC0, 0x64}; 66 67 //Add the string 4http at offset 0x64 and back to pointer 0x00 68 byte[] longArray = Arrays.copyOf(array, 120); 69 longArray[0x64] = 4; 70 longArray[0x64 + 1] = 'h'; 71 longArray[0x64 + 2] = 't'; 72 longArray[0x64 + 3] = 't'; 73 longArray[0x64 + 4] = 'p'; 74 longArray[0x64 + 5] = (byte) 0xC0; 75 longArray[0x64 + 6] = 0x00; 76 77 String fullName = MdnsPacketParser.extractFullName(longArray, 9); 78 assertEquals("gtv.http.iso.", fullName); 79 } 80 81 @Test testExtractFullNameBadPointer()82 public void testExtractFullNameBadPointer() { 83 byte[] array = new byte[]{3, 'i', 's', 'o', 0, (byte) 0xC0, 0x20}; 84 assertThrows( 85 "Setting cursor on negative offset is not allowed.", 86 IllegalArgumentException.class, 87 () -> MdnsPacketParser.extractFullName(array, -10) 88 ); 89 } 90 91 @Test testExtractFullNameLabelTooLong()92 public void testExtractFullNameLabelTooLong() { 93 byte[] longArray = new byte[70]; 94 Arrays.fill(longArray, (byte) 'a'); 95 // Labels maximum allowed size is 64 so this fall in the unknown categorie of 96 // pointer 01xxxxxx or 10xxxxxx 97 longArray[0] = 65; 98 longArray[66] = 0x00; // ther array is [65, 65 times 'a', 0x00] 99 100 assertThrows( 101 "mDNS response packet is badly formed. Not enough data.", 102 IllegalArgumentException.class, 103 () -> MdnsPacketParser.extractFullName(longArray, 0) 104 ); 105 } 106 107 @Test testExtractMatchCriteriaFailureQueryCount()108 public void testExtractMatchCriteriaFailureQueryCount() { 109 //1 query, 2 answers, 0 authority, 0 additional. 110 byte[] array = new byte[]{0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0}; 111 assertThrows( 112 "mDNS response packet contains data that is not answers", 113 IllegalArgumentException.class, 114 () -> MdnsPacketParser.extractMatchCriteria(array) 115 ); 116 } 117 118 @Test testExtractMatchCriteriaFailureAuthorityCount()119 public void testExtractMatchCriteriaFailureAuthorityCount() { 120 //0 query, 2 answers, 2 authority, 0 additional. 121 byte[] array = new byte[]{0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0}; 122 assertThrows( 123 "mDNS response packet contains data that is not answers", 124 IllegalArgumentException.class, 125 () -> MdnsPacketParser.extractMatchCriteria(array) 126 ); 127 } 128 129 @Test testExtractMatchCriteriaFailureAdditionalCount()130 public void testExtractMatchCriteriaFailureAdditionalCount() { 131 //0 query, 2 answers, 0 authority, 3 additional. 132 byte[] array = new byte[]{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3}; 133 assertThrows( 134 "mDNS response packet contains data that is not answers", 135 IllegalArgumentException.class, 136 () -> MdnsPacketParser.extractMatchCriteria(array) 137 ); 138 } 139 140 @Test testExtractMatchCriteriaSuccessOneAnswerLabel()141 public void testExtractMatchCriteriaSuccessOneAnswerLabel() { 142 byte[] array = new byte[]{ 143 0, 0, 0, 0,//Id , Flags 144 0, 0, 0, 1, 0, 0, 0, 0,// Header section. 1 answer. 145 //Data 1 146 3, 'a', 't', 'v', 0x00, //atv. 147 0x00, 0x01, //type A 148 (byte) 0x80, 0x01,//cache flush: True, class: in 149 0, 0, 0, 5,// TTL 5sec 150 0, 4, // Data with size 4 151 100, 80, 40, 20 //ip: 100.80.40.20 152 }; 153 154 List<MatchCriteria> criteria = MdnsPacketParser.extractMatchCriteria(array); 155 156 assertEquals(1, criteria.size()); 157 158 MatchCriteria result = criteria.get(0); 159 assertEquals(12, result.nameOffset); 160 assertEquals(1, result.type); 161 } 162 163 @Test testExtractMatchCriteriaSuccessTwoAnswersPointers()164 public void testExtractMatchCriteriaSuccessTwoAnswersPointers() { 165 byte[] array = new byte[]{ 166 0, 0, 0, 0,//Id , Flags 167 0, 0, 0, 2, 0, 0, 0, 0,// Header section. 2 answers. 168 //Data 1 169 3, 'a', 't', 'v', 0x00, //atv. 170 0x00, 0x01, //type A 171 (byte) 0x80, 0x01,//cache flush: True, class: in 172 0, 0, 0, 5,// TTL 5sec 173 0, 4, // Data with size 4 174 100, 80, 40, 20, //ip: 100.80.40.20 175 //Data 2 176 3, 'g', 't', 'v', (byte) 0b11000000, 12, //gtv.[ptr->]atv. 177 0x00, 16, //type TXT 178 (byte) 0x80, 0x01,//cache flush: True, class: in 179 0, 0, 0, 5,// TTL 5sec 180 0, 3, // Data with size 3 181 'i', 's', 'o' // "iso" 182 }; 183 184 List<MatchCriteria> criteria = MdnsPacketParser.extractMatchCriteria(array); 185 186 assertEquals(2, criteria.size()); 187 188 MatchCriteria result0 = criteria.get(0); 189 assertEquals(12, result0.nameOffset); 190 assertEquals(1, result0.type); 191 192 MatchCriteria result1 = criteria.get(1); 193 assertEquals(31, result1.nameOffset); 194 assertEquals(16, result1.type); 195 } 196 197 @Test testExtractFullName()198 public void testExtractFullName() { 199 byte[] array = new byte[]{ 200 0, 0, 0, 0,//Id , Flags 201 0, 0, 0, 2, 0, 0, 0, 0,// Header section. 2 answers. 202 //Data 1 203 3, 'a', 't', 'v', 0x00, //atv. 204 0x00, 0x01, //type A 205 (byte) 0x80, 0x01,//cache flush: True, class: in 206 0, 0, 0, 5,// TTL 5sec 207 0, 4, // Data with size 4 208 100, 80, 40, 20, //ip: 100.80.40.20 209 //Data 2 210 3, 'g', 't', 'v', (byte) 0b11000000, 12, //gtv.[ptr->]atv. 211 0x00, 16, //type TXT 212 (byte) 0x80, 0x01,//cache flush: True, class: in 213 0, 0, 0, 5,// TTL 5sec 214 0, 3, // Data with size 3 215 'i', 's', 'o' // "iso" 216 }; 217 218 List<MatchCriteria> criteria = MdnsPacketParser.extractMatchCriteria(array); 219 assertEquals(2, criteria.size()); 220 String name0 = MdnsPacketParser.extractFullName(array, criteria.get(0).nameOffset); 221 assertEquals("atv.", name0); 222 String name1 = MdnsPacketParser.extractFullName(array, criteria.get(1).nameOffset); 223 assertEquals("gtv.atv.", name1); 224 } 225 226 @Test testNegativeByteToUint8()227 public void testNegativeByteToUint8() { 228 byte[] array = new byte[]{ 229 0, 0, 0, 0,//Id , Flags 230 0, 0, 0, 3, 0, 0, 0, 0,// Header section. 2 answers. 231 //Data 1 232 3, 'a', 't', 'v', 0x00, //atv. 233 0x00, 0x01, //type A 234 (byte) 0x80, 0x01,//cache flush: True, class: in 235 0, 0, 0, 5,// TTL 5sec 236 0, 4, // Data with size 4 237 100, 80, 40, 20, //ip: 100.80.40.20 238 //Data 2 239 3, 'g', 't', 'v', (byte) 0b11000000, 12, //gtv.[ptr->]atv. 240 0x00, 16, //type TXT 241 (byte) 0x80, 0x01,//cache flush: True, class: in 242 0, 0, 0, 5,// TTL 5sec 243 0, (byte) 130, // Data with size 130 > 127 244 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 245 //Data 3 246 4, 'f', 'a', 'i', 'l', 0x00, //fail. 247 0x00, 0x01, //type A 248 (byte) 0x80, 0x01,//cache flush: True, class: in 249 0, 0, 0, 5,// TTL 5sec 250 0, 4, // Data with size 4 251 100, 80, 40, 20, //ip: 100.80.40.20 252 }; 253 254 List<MatchCriteria> criteria = MdnsPacketParser.extractMatchCriteria(array); 255 assertEquals(3, criteria.size()); 256 String name2 = MdnsPacketParser.extractFullName(array, criteria.get(2).nameOffset); 257 assertEquals("fail.", name2); 258 } 259 260 @Test testExtractMatchCriteriaFailureTooMuchData()261 public void testExtractMatchCriteriaFailureTooMuchData() { 262 byte[] array = new byte[]{ 263 0, 0, 0, 0,//Id , Flags 264 0, 0, 0, 1, 0, 0, 0, 0,// Header section. 2 answers. 265 //Data 1 266 3, 'a', 't', 'v', 0x00, //atv. 267 0x00, 0x01, //type A 268 (byte) 0x80, 0x01,//cache flush: True, class: in 269 0, 0, 0, 5,// TTL 5sec 270 0, 4, // Data with size 4 271 100, 80, 40, 20, //ip: 100.80.40.20 272 //extra data. 273 'e','x','t','r','a' 274 }; 275 276 assertThrows( 277 "mDNS response packet is badly formed. Too much data.", 278 IllegalArgumentException.class, 279 () -> MdnsPacketParser.extractMatchCriteria(array) 280 ); 281 } 282 283 }