blob: 876d95b7fa501a39eb6ea0a256fc4db4a5090a75 [file] [log] [blame]
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +09001/*
2 * Copyright (C) 2015 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
17package android.net.dhcp;
18
19import android.net.NetworkUtils;
Lorenzo Colittid9735372015-06-02 13:15:50 +090020import android.net.DhcpResults;
Lorenzo Colittif68edb12015-06-02 17:46:24 +090021import android.net.LinkAddress;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090022import android.system.OsConstants;
23import android.test.suitebuilder.annotation.SmallTest;
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090024import com.android.internal.util.HexDump;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090025
26import java.net.Inet4Address;
27import java.nio.ByteBuffer;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090028import java.util.ArrayList;
29
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090030import junit.framework.TestCase;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090031import libcore.util.HexEncoding;
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090032import java.util.Arrays;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090033
34import static android.net.dhcp.DhcpPacket.*;
35
36
37public class DhcpPacketTest extends TestCase {
38
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090039 private static Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
40 private static Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090041 // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
42 // doesn't use == instead of equals when comparing addresses.
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090043 private static Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090044
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090045 private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
46
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090047 private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
48 return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
49 }
50
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090051 public void setUp() {
52 DhcpPacket.testOverrideVendorId = "android-dhcp-???";
53 DhcpPacket.testOverrideHostname = "android-01234567890abcde";
54 }
55
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090056 class TestDhcpPacket extends DhcpPacket {
57 private byte mType;
58 // TODO: Make this a map of option numbers to bytes instead.
Lorenzo Colittif68edb12015-06-02 17:46:24 +090059 private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090060
Lorenzo Colittif68edb12015-06-02 17:46:24 +090061 public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
62 super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
Lorenzo Colitti3e979322015-04-21 15:22:17 +090063 CLIENT_MAC, true);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090064 mType = type;
Lorenzo Colittid9735372015-06-02 13:15:50 +090065 }
66
Lorenzo Colittif68edb12015-06-02 17:46:24 +090067 public TestDhcpPacket(byte type) {
68 this(type, INADDR_ANY, CLIENT_ADDR);
69 }
70
Lorenzo Colittid9735372015-06-02 13:15:50 +090071 public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090072 mDomainBytes = domainBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +090073 return this;
74 }
75
76 public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090077 mVendorInfoBytes = vendorInfoBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +090078 return this;
79 }
80
81 public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
82 mLeaseTimeBytes = leaseTimeBytes;
83 return this;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090084 }
85
Lorenzo Colittif68edb12015-06-02 17:46:24 +090086 public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
87 mNetmaskBytes = netmaskBytes;
88 return this;
89 }
90
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090091 public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
92 ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
93 fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
94 DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
95 return result;
96 }
97
98 public void finishPacket(ByteBuffer buffer) {
99 addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
100 if (mDomainBytes != null) {
101 addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
102 }
103 if (mVendorInfoBytes != null) {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900104 addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900105 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900106 if (mLeaseTimeBytes != null) {
107 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
108 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900109 if (mNetmaskBytes != null) {
110 addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
111 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900112 addTlvEnd(buffer);
113 }
114
115 // Convenience method.
116 public ByteBuffer build() {
117 // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
118 ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
119 pkt.flip();
120 return pkt;
121 }
122 }
123
124 private void assertDomainAndVendorInfoParses(
125 String expectedDomain, byte[] domainBytes,
Erik Kline496906e2015-09-16 15:44:54 +0900126 String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900127 ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
128 .setDomainBytes(domainBytes)
129 .setVendorInfoBytes(vendorInfoBytes)
130 .build();
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900131 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
132 assertEquals(expectedDomain, offerPacket.mDomainName);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900133 assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900134 }
135
136 @SmallTest
137 public void testDomainName() throws Exception {
138 byte[] nullByte = new byte[] { 0x00 };
139 byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
140 byte[] nonNullDomain = new byte[] {
141 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
142 };
143 byte[] trailingNullDomain = new byte[] {
144 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
145 };
146 byte[] embeddedNullsDomain = new byte[] {
147 (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
148 };
149 byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
150
151 byte[] meteredEmbeddedNull = metered.clone();
152 meteredEmbeddedNull[7] = (char) 0;
153
154 byte[] meteredTrailingNull = metered.clone();
155 meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
156
157 assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
158 assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
159 assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
160 assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
161 "ANDROID\u0000METERED", meteredEmbeddedNull);
162 assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
163 "ANDROID_METERE\u0000", meteredTrailingNull);
164 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900165
166 private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
Erik Kline496906e2015-09-16 15:44:54 +0900167 long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900168 TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
169 if (leaseTimeBytes != null) {
170 testPacket.setLeaseTimeBytes(leaseTimeBytes);
171 }
172 ByteBuffer packet = testPacket.build();
Erik Kline496906e2015-09-16 15:44:54 +0900173 DhcpPacket offerPacket = null;
174
Lorenzo Colittid9735372015-06-02 13:15:50 +0900175 if (!expectValid) {
Erik Kline496906e2015-09-16 15:44:54 +0900176 try {
177 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
178 fail("Invalid packet parsed successfully: " + offerPacket);
179 } catch (ParseException expected) {
180 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900181 return;
182 }
Erik Kline496906e2015-09-16 15:44:54 +0900183
184 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
185 assertNotNull(offerPacket);
Lorenzo Colittid9735372015-06-02 13:15:50 +0900186 assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
187 DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
188 assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
189 }
190
191 @SmallTest
192 public void testLeaseTime() throws Exception {
193 byte[] noLease = null;
194 byte[] tooShortLease = new byte[] { 0x00, 0x00 };
195 byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
196 byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
197 byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
198 byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
199 byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
200 byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
201 byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
202 byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
203
204 assertLeaseTimeParses(true, null, 0, noLease);
205 assertLeaseTimeParses(false, null, 0, tooShortLease);
206 assertLeaseTimeParses(false, null, 0, tooLongLease);
207 assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
208 assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
209 assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
210 assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
211 assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
212 assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
213 assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
214 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900215
216 private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900217 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900218 checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
219 checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
220 }
221
222 private void checkIpAddress(String expected, byte type,
223 Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900224 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900225 ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
226 .setNetmaskBytes(netmaskBytes)
227 .build();
228 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
229 DhcpResults results = offerPacket.toDhcpResults();
230
231 if (expected != null) {
232 LinkAddress expectedAddress = new LinkAddress(expected);
233 assertEquals(expectedAddress, results.ipAddress);
234 } else {
235 assertNull(results);
236 }
237 }
238
239 @SmallTest
240 public void testIpAddress() throws Exception {
241 byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
242 byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
243 byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900244 Inet4Address example1 = v4Address("192.0.2.1");
245 Inet4Address example2 = v4Address("192.0.2.43");
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900246
247 // A packet without any addresses is not valid.
248 checkIpAddress(null, ANY, ANY, slash24Netmask);
249
250 // ClientIP is used iff YourIP is not present.
251 checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
252 checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
253 checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
254
255 // Invalid netmasks are ignored.
256 checkIpAddress(null, example2, ANY, invalidNetmask);
257
258 // If there is no netmask, implicit netmasks are used.
259 checkIpAddress("192.0.2.43/24", ANY, example2, null);
260 }
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900261
262 private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
263 String domains, String serverAddress, String vendorInfo, int leaseDuration,
264 boolean hasMeteredHint, DhcpResults dhcpResults) throws Exception {
265 assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
266 assertEquals(v4Address(gateway), dhcpResults.gateway);
267
268 String[] dnsServerStrings = dnsServersString.split(",");
269 ArrayList dnsServers = new ArrayList();
270 for (String dnsServerString : dnsServerStrings) {
271 dnsServers.add(v4Address(dnsServerString));
272 }
273 assertEquals(dnsServers, dhcpResults.dnsServers);
274
275 assertEquals(domains, dhcpResults.domains);
276 assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
277 assertEquals(vendorInfo, dhcpResults.vendorInfo);
278 assertEquals(leaseDuration, dhcpResults.leaseDuration);
279 assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
280 }
281
282 @SmallTest
283 public void testOffer1() throws Exception {
284 // TODO: Turn all of these into golden files. This will probably require modifying
285 // Android.mk appropriately, making this into an AndroidTestCase, and adding code to read
286 // the golden files from the test APK's assets via mContext.getAssets().
287 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
288 // IP header.
289 "451001480000000080118849c0a89003c0a89ff7" +
290 // UDP header.
291 "004300440134dcfa" +
292 // BOOTP header.
293 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
294 // MAC address.
295 "30766ff2a90c00000000000000000000" +
296 // Server name.
297 "0000000000000000000000000000000000000000000000000000000000000000" +
298 "0000000000000000000000000000000000000000000000000000000000000000" +
299 // File.
300 "0000000000000000000000000000000000000000000000000000000000000000" +
301 "0000000000000000000000000000000000000000000000000000000000000000" +
302 "0000000000000000000000000000000000000000000000000000000000000000" +
303 "0000000000000000000000000000000000000000000000000000000000000000" +
304 // Options
305 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
306 "3a0400000e103b040000189cff00000000000000000000"
307 ).toCharArray(), false));
308
309 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
310 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
311 DhcpResults dhcpResults = offerPacket.toDhcpResults();
312 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
313 null, "192.168.144.3", null, 7200, false, dhcpResults);
314 }
315
316 @SmallTest
317 public void testOffer2() throws Exception {
318 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
319 // IP header.
320 "450001518d0600004011144dc0a82b01c0a82bf7" +
321 // UDP header.
322 "00430044013d9ac7" +
323 // BOOTP header.
324 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
325 // MAC address.
326 "30766ff2a90c00000000000000000000" +
327 // Server name.
328 "0000000000000000000000000000000000000000000000000000000000000000" +
329 "0000000000000000000000000000000000000000000000000000000000000000" +
330 // File.
331 "0000000000000000000000000000000000000000000000000000000000000000" +
332 "0000000000000000000000000000000000000000000000000000000000000000" +
333 "0000000000000000000000000000000000000000000000000000000000000000" +
334 "0000000000000000000000000000000000000000000000000000000000000000" +
335 // Options
336 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
337 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
338 ).toCharArray(), false));
339
340 assertEquals(337, packet.limit());
341 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
342 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
343 DhcpResults dhcpResults = offerPacket.toDhcpResults();
344 assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
345 null, "192.168.43.1", "ANDROID_METERED", 3600, true, dhcpResults);
346 assertTrue(dhcpResults.hasMeteredHint());
347 }
348
349 @SmallTest
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900350 public void testBadHwaddrLength() throws Exception {
351 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
352 // IP header.
353 "450001518d0600004011144dc0a82b01c0a82bf7" +
354 // UDP header.
355 "00430044013d9ac7" +
356 // BOOTP header.
357 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
358 // MAC address.
359 "30766ff2a90c00000000000000000000" +
360 // Server name.
361 "0000000000000000000000000000000000000000000000000000000000000000" +
362 "0000000000000000000000000000000000000000000000000000000000000000" +
363 // File.
364 "0000000000000000000000000000000000000000000000000000000000000000" +
365 "0000000000000000000000000000000000000000000000000000000000000000" +
366 "0000000000000000000000000000000000000000000000000000000000000000" +
367 "0000000000000000000000000000000000000000000000000000000000000000" +
368 // Options
369 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
370 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
371 ).toCharArray(), false));
372 String expectedClientMac = "30766FF2A90C";
373
374 final int hwAddrLenOffset = 20 + 8 + 2;
375 assertEquals(6, packet.get(hwAddrLenOffset));
376
377 // Expect the expected.
378 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
379 assertNotNull(offerPacket);
380 assertEquals(6, offerPacket.getClientMac().length);
381 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
382
383 // Reduce the hardware address length and verify that it shortens the client MAC.
384 packet.flip();
385 packet.put(hwAddrLenOffset, (byte) 5);
386 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
387 assertNotNull(offerPacket);
388 assertEquals(5, offerPacket.getClientMac().length);
389 assertEquals(expectedClientMac.substring(0, 10),
390 HexDump.toHexString(offerPacket.getClientMac()));
391
392 packet.flip();
393 packet.put(hwAddrLenOffset, (byte) 3);
394 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
395 assertNotNull(offerPacket);
396 assertEquals(3, offerPacket.getClientMac().length);
397 assertEquals(expectedClientMac.substring(0, 6),
398 HexDump.toHexString(offerPacket.getClientMac()));
399
400 // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
401 // and crash, and b) hardcode it to 6.
402 packet.flip();
403 packet.put(hwAddrLenOffset, (byte) -1);
404 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
405 assertNotNull(offerPacket);
406 assertEquals(6, offerPacket.getClientMac().length);
407 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
408
409 // Set the the hardware address length to a positive invalid value (> 16) and verify that we
410 // hardcode it to 6.
411 packet.flip();
412 packet.put(hwAddrLenOffset, (byte) 17);
413 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
414 assertNotNull(offerPacket);
415 assertEquals(6, offerPacket.getClientMac().length);
416 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
417 }
418
419 @SmallTest
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900420 public void testPadAndOverloadedOptionsOffer() throws Exception {
421 // A packet observed in the real world that is interesting for two reasons:
422 //
423 // 1. It uses pad bytes, which we previously didn't support correctly.
424 // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
425 // store any information in the overloaded fields).
426 //
427 // For now, we just check that it parses correctly.
428 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
429 // Ethernet header.
430 "b4cef6000000e80462236e300800" +
431 // IP header.
432 "4500014c00000000ff11741701010101ac119876" +
433 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
434 "004300440138ae5a" +
435 // BOOTP header.
436 "020106000fa0059f0000000000000000ac1198760000000000000000" +
437 // MAC address.
438 "b4cef600000000000000000000000000" +
439 // Server name.
440 "ff00000000000000000000000000000000000000000000000000000000000000" +
441 "0000000000000000000000000000000000000000000000000000000000000000" +
442 // File.
443 "ff00000000000000000000000000000000000000000000000000000000000000" +
444 "0000000000000000000000000000000000000000000000000000000000000000" +
445 "0000000000000000000000000000000000000000000000000000000000000000" +
446 "0000000000000000000000000000000000000000000000000000000000000000" +
447 // Options
448 "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
449 "0000000000000000000000000000000000000000000000ff000000"
450 ).toCharArray(), false));
451
452 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
453 assertTrue(offerPacket instanceof DhcpOfferPacket);
454 DhcpResults dhcpResults = offerPacket.toDhcpResults();
455 assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
456 null, "1.1.1.1", null, 43200, false, dhcpResults);
457 }
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900458
459 @SmallTest
460 public void testBug2111() throws Exception {
461 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
462 // IP header.
463 "4500014c00000000ff119beac3eaf3880a3f5d04" +
464 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
465 "0043004401387464" +
466 // BOOTP header.
467 "0201060002554812000a0000000000000a3f5d040000000000000000" +
468 // MAC address.
469 "00904c00000000000000000000000000" +
470 // Server name.
471 "0000000000000000000000000000000000000000000000000000000000000000" +
472 "0000000000000000000000000000000000000000000000000000000000000000" +
473 // File.
474 "0000000000000000000000000000000000000000000000000000000000000000" +
475 "0000000000000000000000000000000000000000000000000000000000000000" +
476 "0000000000000000000000000000000000000000000000000000000000000000" +
477 "0000000000000000000000000000000000000000000000000000000000000000" +
478 // Options.
479 "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
480 "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"
481 ).toCharArray(), false));
482
483 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
484 assertTrue(offerPacket instanceof DhcpOfferPacket);
485 DhcpResults dhcpResults = offerPacket.toDhcpResults();
486 assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
487 "domain123.co.uk", "192.0.2.254", null, 49094, false, dhcpResults);
488 }
489
490 @SmallTest
491 public void testBug2136() throws Exception {
492 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
493 // Ethernet header.
494 "bcf5ac000000d0c7890000000800" +
495 // IP header.
496 "4500014c00000000ff119beac3eaf3880a3f5d04" +
497 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
498 "0043004401387574" +
499 // BOOTP header.
500 "0201060163339a3000050000000000000a209ecd0000000000000000" +
501 // MAC address.
502 "bcf5ac00000000000000000000000000" +
503 // Server name.
504 "0000000000000000000000000000000000000000000000000000000000000000" +
505 "0000000000000000000000000000000000000000000000000000000000000000" +
506 // File.
507 "0000000000000000000000000000000000000000000000000000000000000000" +
508 "0000000000000000000000000000000000000000000000000000000000000000" +
509 "0000000000000000000000000000000000000000000000000000000000000000" +
510 "0000000000000000000000000000000000000000000000000000000000000000" +
511 // Options.
512 "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
513 "0f0b6c616e63732e61632e756b000000000000000000ff00000000"
514 ).toCharArray(), false));
515
516 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
517 assertTrue(offerPacket instanceof DhcpOfferPacket);
518 assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
519 DhcpResults dhcpResults = offerPacket.toDhcpResults();
520 assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
521 "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults);
522 }
Erik Klineb19238c2015-10-06 18:43:17 +0900523
524 @SmallTest
525 public void testUdpServerAnySourcePort() throws Exception {
526 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
527 // Ethernet header.
528 "9cd917000000001c2e0000000800" +
529 // IP header.
530 "45a00148000040003d115087d18194fb0a0f7af2" +
531 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
532 // NOTE: The server source port is not the canonical port 67.
533 "C29F004401341268" +
534 // BOOTP header.
535 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
536 // MAC address.
537 "9cd91700000000000000000000000000" +
538 // Server name.
539 "0000000000000000000000000000000000000000000000000000000000000000" +
540 "0000000000000000000000000000000000000000000000000000000000000000" +
541 // File.
542 "0000000000000000000000000000000000000000000000000000000000000000" +
543 "0000000000000000000000000000000000000000000000000000000000000000" +
544 "0000000000000000000000000000000000000000000000000000000000000000" +
545 "0000000000000000000000000000000000000000000000000000000000000000" +
546 // Options.
547 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
548 "d18180060f0777766d2e6564751c040a0fffffff000000"
549 ).toCharArray(), false));
550
551 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
552 assertTrue(offerPacket instanceof DhcpOfferPacket);
553 assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
554 DhcpResults dhcpResults = offerPacket.toDhcpResults();
555 assertDhcpResults("10.15.122.242/16", "10.15.200.23",
556 "209.129.128.3,209.129.148.3,209.129.128.6",
557 "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults);
558 }
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900559
560 @SmallTest
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900561 public void testUdpInvalidDstPort() throws Exception {
562 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
563 // Ethernet header.
564 "9cd917000000001c2e0000000800" +
565 // IP header.
566 "45a00148000040003d115087d18194fb0a0f7af2" +
567 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
568 // NOTE: The destination port is a non-DHCP port.
569 "0043aaaa01341268" +
570 // BOOTP header.
571 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
572 // MAC address.
573 "9cd91700000000000000000000000000" +
574 // Server name.
575 "0000000000000000000000000000000000000000000000000000000000000000" +
576 "0000000000000000000000000000000000000000000000000000000000000000" +
577 // File.
578 "0000000000000000000000000000000000000000000000000000000000000000" +
579 "0000000000000000000000000000000000000000000000000000000000000000" +
580 "0000000000000000000000000000000000000000000000000000000000000000" +
581 "0000000000000000000000000000000000000000000000000000000000000000" +
582 // Options.
583 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
584 "d18180060f0777766d2e6564751c040a0fffffff000000"
585 ).toCharArray(), false));
586
587 try {
588 DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
589 fail("Packet with invalid dst port did not throw ParseException");
590 } catch (ParseException expected) {}
591 }
592
593 @SmallTest
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900594 public void testMultipleRouters() throws Exception {
595 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
596 // Ethernet header.
597 "fc3d93000000" + "081735000000" + "0800" +
598 // IP header.
599 "45000148c2370000ff117ac2c0a8bd02ffffffff" +
600 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
601 "0043004401343beb" +
602 // BOOTP header.
603 "0201060027f518e20000800000000000c0a8bd310000000000000000" +
604 // MAC address.
605 "fc3d9300000000000000000000000000" +
606 // Server name.
607 "0000000000000000000000000000000000000000000000000000000000000000" +
608 "0000000000000000000000000000000000000000000000000000000000000000" +
609 // File.
610 "0000000000000000000000000000000000000000000000000000000000000000" +
611 "0000000000000000000000000000000000000000000000000000000000000000" +
612 "0000000000000000000000000000000000000000000000000000000000000000" +
613 "0000000000000000000000000000000000000000000000000000000000000000" +
614 // Options.
615 "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
616 "0308c0a8bd01ffffff0006080808080808080404ff000000000000"
617 ).toCharArray(), false));
618
619 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
620 assertTrue(offerPacket instanceof DhcpOfferPacket);
621 assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
622 DhcpResults dhcpResults = offerPacket.toDhcpResults();
623 assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
624 null, "192.171.189.2", null, 28800, false, dhcpResults);
625 }
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900626
627 @SmallTest
628 public void testDiscoverPacket() throws Exception {
629 short secs = 7;
630 int transactionId = 0xdeadbeef;
631 byte[] hwaddr = {
632 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
633 };
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900634
635 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
636 DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
Erik Kline1d511ab2016-03-24 17:30:13 +0900637 false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900638
639 byte[] headers = new byte[] {
640 // Ethernet header.
641 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
642 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
643 (byte) 0x08, (byte) 0x00,
644 // IP header.
Erik Kline1d511ab2016-03-24 17:30:13 +0900645 (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900646 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
Erik Kline1d511ab2016-03-24 17:30:13 +0900647 (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900648 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
649 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
650 // UDP header.
651 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
Erik Kline1d511ab2016-03-24 17:30:13 +0900652 (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900653 // BOOTP.
654 (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
655 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
656 (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
657 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
658 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
659 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
660 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
661 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
662 (byte) 0xb1, (byte) 0x7a
663 };
664 byte[] options = new byte[] {
665 // Magic cookie 0x63825363.
666 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
667 // Message type DISCOVER.
668 (byte) 0x35, (byte) 0x01, (byte) 0x01,
669 // Client identifier Ethernet, da:01:19:5b:b1:7a.
670 (byte) 0x3d, (byte) 0x07,
671 (byte) 0x01,
672 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
673 // Max message size 1500.
674 (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
675 // Version "android-dhcp-???".
676 (byte) 0x3c, (byte) 0x10,
677 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
678 // Hostname "android-01234567890abcde"
679 (byte) 0x0c, (byte) 0x18,
680 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
681 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
682 // Requested parameter list.
Erik Kline1d511ab2016-03-24 17:30:13 +0900683 (byte) 0x37, (byte) 0x0a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900684 DHCP_SUBNET_MASK,
685 DHCP_ROUTER,
686 DHCP_DNS_SERVER,
687 DHCP_DOMAIN_NAME,
688 DHCP_MTU,
Erik Kline1d511ab2016-03-24 17:30:13 +0900689 DHCP_BROADCAST_ADDRESS,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900690 DHCP_LEASE_TIME,
Erik Kline1d511ab2016-03-24 17:30:13 +0900691 DHCP_RENEWAL_TIME,
692 DHCP_REBINDING_TIME,
693 DHCP_VENDOR_INFO,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900694 // End options.
695 (byte) 0xff,
696 // Our packets are always of even length. TODO: find out why and possibly fix it.
697 (byte) 0x00
698 };
699 byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
700 assertTrue((expected.length & 1) == 0);
701 System.arraycopy(headers, 0, expected, 0, headers.length);
702 System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
703
704 byte[] actual = new byte[packet.limit()];
705 packet.get(actual);
706 String msg =
707 "Expected:\n " + Arrays.toString(expected) +
708 "\nActual:\n " + Arrays.toString(actual);
709 assertTrue(msg, Arrays.equals(expected, actual));
710 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900711}