blob: 5f863ef2f4b13d8fed80b8140d6d308cf3bb0856 [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
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090019import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
20import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
21import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
22import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
23import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_ACK;
24import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_OFFER;
25import static android.net.dhcp.DhcpPacket.DHCP_MTU;
26import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
27import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
28import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
29import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
30import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
31import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
32import static android.net.dhcp.DhcpPacket.ENCAP_L2;
33import static android.net.dhcp.DhcpPacket.ENCAP_L3;
34import static android.net.dhcp.DhcpPacket.INADDR_ANY;
35import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
36import static android.net.dhcp.DhcpPacket.ParseException;
37import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
38import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
39
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090040import static org.junit.Assert.assertEquals;
41import static org.junit.Assert.assertNotNull;
42import static org.junit.Assert.assertNull;
43import static org.junit.Assert.assertTrue;
44import static org.junit.Assert.fail;
45
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090046import android.annotation.Nullable;
Lorenzo Colittid9735372015-06-02 13:15:50 +090047import android.net.DhcpResults;
Lorenzo Colittif68edb12015-06-02 17:46:24 +090048import android.net.LinkAddress;
Hugo Benichie0ea7fe2016-10-05 18:33:21 +090049import android.net.NetworkUtils;
50import android.net.metrics.DhcpErrorEvent;
Brett Chabot84151d92019-02-27 15:37:59 -080051
52import androidx.test.filters.SmallTest;
53import androidx.test.runner.AndroidJUnit4;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090054
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090055import com.android.internal.util.HexDump;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090056
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090057import org.junit.Before;
58import org.junit.Test;
59import org.junit.runner.RunWith;
60
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090061import java.io.ByteArrayOutputStream;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090062import java.net.Inet4Address;
63import java.nio.ByteBuffer;
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090064import java.nio.charset.Charset;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090065import java.util.ArrayList;
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090066import java.util.Arrays;
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090067import java.util.Collections;
Hugo Benichi006e0612016-10-05 21:07:19 +090068import java.util.Random;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090069
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090070@RunWith(AndroidJUnit4.class)
71@SmallTest
72public class DhcpPacketTest {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090073
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090074 private static final Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
75 private static final Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
76 private static final int PREFIX_LENGTH = 22;
77 private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
78 private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
79 SERVER_ADDR, PREFIX_LENGTH);
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090080 private static final String HOSTNAME = "testhostname";
81 private static final short MTU = 1500;
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090082 // Use our own empty address instead of IPV4_ADDR_ANY or INADDR_ANY to ensure that the code
Lorenzo Colittif68edb12015-06-02 17:46:24 +090083 // doesn't use == instead of equals when comparing addresses.
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090084 private static final Inet4Address ANY = v4Address("0.0.0.0");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090085
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090086 private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090087
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090088 private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
89 return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
90 }
91
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090092 @Before
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090093 public void setUp() {
94 DhcpPacket.testOverrideVendorId = "android-dhcp-???";
95 DhcpPacket.testOverrideHostname = "android-01234567890abcde";
96 }
97
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090098 class TestDhcpPacket extends DhcpPacket {
99 private byte mType;
100 // TODO: Make this a map of option numbers to bytes instead.
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900101 private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900102
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900103 public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
104 super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900105 CLIENT_MAC, true);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900106 mType = type;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900107 }
108
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900109 public TestDhcpPacket(byte type) {
110 this(type, INADDR_ANY, CLIENT_ADDR);
111 }
112
Lorenzo Colittid9735372015-06-02 13:15:50 +0900113 public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900114 mDomainBytes = domainBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900115 return this;
116 }
117
118 public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900119 mVendorInfoBytes = vendorInfoBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900120 return this;
121 }
122
123 public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
124 mLeaseTimeBytes = leaseTimeBytes;
125 return this;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900126 }
127
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900128 public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
129 mNetmaskBytes = netmaskBytes;
130 return this;
131 }
132
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900133 public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
134 ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
135 fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
136 DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
137 return result;
138 }
139
140 public void finishPacket(ByteBuffer buffer) {
141 addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
142 if (mDomainBytes != null) {
143 addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
144 }
145 if (mVendorInfoBytes != null) {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900146 addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900147 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900148 if (mLeaseTimeBytes != null) {
149 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
150 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900151 if (mNetmaskBytes != null) {
152 addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
153 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900154 addTlvEnd(buffer);
155 }
156
157 // Convenience method.
158 public ByteBuffer build() {
159 // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
160 ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
161 pkt.flip();
162 return pkt;
163 }
164 }
165
166 private void assertDomainAndVendorInfoParses(
167 String expectedDomain, byte[] domainBytes,
Erik Kline496906e2015-09-16 15:44:54 +0900168 String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900169 ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
170 .setDomainBytes(domainBytes)
171 .setVendorInfoBytes(vendorInfoBytes)
172 .build();
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900173 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
174 assertEquals(expectedDomain, offerPacket.mDomainName);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900175 assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900176 }
177
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900178 @Test
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900179 public void testDomainName() throws Exception {
180 byte[] nullByte = new byte[] { 0x00 };
181 byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
182 byte[] nonNullDomain = new byte[] {
183 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
184 };
185 byte[] trailingNullDomain = new byte[] {
186 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
187 };
188 byte[] embeddedNullsDomain = new byte[] {
189 (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
190 };
191 byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
192
193 byte[] meteredEmbeddedNull = metered.clone();
194 meteredEmbeddedNull[7] = (char) 0;
195
196 byte[] meteredTrailingNull = metered.clone();
197 meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
198
199 assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
200 assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
201 assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
202 assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
203 "ANDROID\u0000METERED", meteredEmbeddedNull);
204 assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
205 "ANDROID_METERE\u0000", meteredTrailingNull);
206 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900207
208 private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
Erik Kline496906e2015-09-16 15:44:54 +0900209 long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900210 TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
211 if (leaseTimeBytes != null) {
212 testPacket.setLeaseTimeBytes(leaseTimeBytes);
213 }
214 ByteBuffer packet = testPacket.build();
Erik Kline496906e2015-09-16 15:44:54 +0900215 DhcpPacket offerPacket = null;
216
Lorenzo Colittid9735372015-06-02 13:15:50 +0900217 if (!expectValid) {
Erik Kline496906e2015-09-16 15:44:54 +0900218 try {
219 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
220 fail("Invalid packet parsed successfully: " + offerPacket);
221 } catch (ParseException expected) {
222 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900223 return;
224 }
Erik Kline496906e2015-09-16 15:44:54 +0900225
226 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
227 assertNotNull(offerPacket);
Lorenzo Colittid9735372015-06-02 13:15:50 +0900228 assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
229 DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
230 assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
231 }
232
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900233 @Test
Lorenzo Colittid9735372015-06-02 13:15:50 +0900234 public void testLeaseTime() throws Exception {
235 byte[] noLease = null;
236 byte[] tooShortLease = new byte[] { 0x00, 0x00 };
237 byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
238 byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
239 byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
240 byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
241 byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
242 byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
243 byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
244 byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
245
246 assertLeaseTimeParses(true, null, 0, noLease);
247 assertLeaseTimeParses(false, null, 0, tooShortLease);
248 assertLeaseTimeParses(false, null, 0, tooLongLease);
249 assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
250 assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
251 assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
252 assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
253 assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
254 assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
255 assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
256 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900257
258 private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900259 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900260 checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
261 checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
262 }
263
264 private void checkIpAddress(String expected, byte type,
265 Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900266 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900267 ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
268 .setNetmaskBytes(netmaskBytes)
269 .build();
270 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
271 DhcpResults results = offerPacket.toDhcpResults();
272
273 if (expected != null) {
274 LinkAddress expectedAddress = new LinkAddress(expected);
275 assertEquals(expectedAddress, results.ipAddress);
276 } else {
277 assertNull(results);
278 }
279 }
280
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900281 @Test
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900282 public void testIpAddress() throws Exception {
283 byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
284 byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
285 byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900286 Inet4Address example1 = v4Address("192.0.2.1");
287 Inet4Address example2 = v4Address("192.0.2.43");
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900288
289 // A packet without any addresses is not valid.
290 checkIpAddress(null, ANY, ANY, slash24Netmask);
291
292 // ClientIP is used iff YourIP is not present.
293 checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
294 checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
295 checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
296
297 // Invalid netmasks are ignored.
298 checkIpAddress(null, example2, ANY, invalidNetmask);
299
300 // If there is no netmask, implicit netmasks are used.
301 checkIpAddress("192.0.2.43/24", ANY, example2, null);
302 }
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900303
304 private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900305 String domains, String serverAddress, String serverHostName, String vendorInfo,
306 int leaseDuration, boolean hasMeteredHint, int mtu, DhcpResults dhcpResults)
307 throws Exception {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900308 assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
309 assertEquals(v4Address(gateway), dhcpResults.gateway);
310
311 String[] dnsServerStrings = dnsServersString.split(",");
312 ArrayList dnsServers = new ArrayList();
313 for (String dnsServerString : dnsServerStrings) {
314 dnsServers.add(v4Address(dnsServerString));
315 }
316 assertEquals(dnsServers, dhcpResults.dnsServers);
317
318 assertEquals(domains, dhcpResults.domains);
319 assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900320 assertEquals(serverHostName, dhcpResults.serverHostName);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900321 assertEquals(vendorInfo, dhcpResults.vendorInfo);
322 assertEquals(leaseDuration, dhcpResults.leaseDuration);
323 assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900324 assertEquals(mtu, dhcpResults.mtu);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900325 }
326
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900327 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900328 public void testOffer1() throws Exception {
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900329 // TODO: Turn all of these into golden files. This will probably require using
Brett Chabot84151d92019-02-27 15:37:59 -0800330 // androidx.test.InstrumentationRegistry for obtaining a Context object
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900331 // to read such golden files, along with an appropriate Android.mk.
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900332 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900333 // IP header.
334 "451001480000000080118849c0a89003c0a89ff7" +
335 // UDP header.
336 "004300440134dcfa" +
337 // BOOTP header.
338 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
339 // MAC address.
340 "30766ff2a90c00000000000000000000" +
341 // Server name.
342 "0000000000000000000000000000000000000000000000000000000000000000" +
343 "0000000000000000000000000000000000000000000000000000000000000000" +
344 // File.
345 "0000000000000000000000000000000000000000000000000000000000000000" +
346 "0000000000000000000000000000000000000000000000000000000000000000" +
347 "0000000000000000000000000000000000000000000000000000000000000000" +
348 "0000000000000000000000000000000000000000000000000000000000000000" +
349 // Options
350 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900351 "3a0400000e103b040000189cff00000000000000000000"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900352
353 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
354 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
355 DhcpResults dhcpResults = offerPacket.toDhcpResults();
356 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900357 null, "192.168.144.3", "", null, 7200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900358 }
359
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900360 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900361 public void testOffer2() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900362 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900363 // IP header.
364 "450001518d0600004011144dc0a82b01c0a82bf7" +
365 // UDP header.
366 "00430044013d9ac7" +
367 // BOOTP header.
368 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
369 // MAC address.
370 "30766ff2a90c00000000000000000000" +
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900371 // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator).
372 "646863702e616e64726f69642e636f6d00000000000000000000000000000000" +
373 "0000000000004141414100000000000000000000000000000000000000000000" +
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900374 // File.
375 "0000000000000000000000000000000000000000000000000000000000000000" +
376 "0000000000000000000000000000000000000000000000000000000000000000" +
377 "0000000000000000000000000000000000000000000000000000000000000000" +
378 "0000000000000000000000000000000000000000000000000000000000000000" +
379 // Options
380 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900381 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900382
383 assertEquals(337, packet.limit());
384 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
385 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
386 DhcpResults dhcpResults = offerPacket.toDhcpResults();
387 assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900388 null, "192.168.43.1", "dhcp.android.com", "ANDROID_METERED", 3600, true, 0,
389 dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900390 assertTrue(dhcpResults.hasMeteredHint());
391 }
392
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900393 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900394 public void testBadIpPacket() throws Exception {
395 final byte[] packet = HexDump.hexStringToByteArray(
396 // IP header.
397 "450001518d0600004011144dc0a82b01c0a82bf7");
398
399 try {
400 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
401 } catch (DhcpPacket.ParseException expected) {
402 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
403 return;
404 }
405 fail("Dhcp packet parsing should have failed");
406 }
407
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900408 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900409 public void testBadDhcpPacket() throws Exception {
410 final byte[] packet = HexDump.hexStringToByteArray(
411 // IP header.
412 "450001518d0600004011144dc0a82b01c0a82bf7" +
413 // UDP header.
414 "00430044013d9ac7" +
415 // BOOTP header.
416 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
417
418 try {
419 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
420 } catch (DhcpPacket.ParseException expected) {
421 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
422 return;
423 }
424 fail("Dhcp packet parsing should have failed");
425 }
426
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900427 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900428 public void testBadTruncatedOffer() throws Exception {
429 final byte[] packet = HexDump.hexStringToByteArray(
430 // IP header.
431 "450001518d0600004011144dc0a82b01c0a82bf7" +
432 // UDP header.
433 "00430044013d9ac7" +
434 // BOOTP header.
435 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
436 // MAC address.
437 "30766ff2a90c00000000000000000000" +
438 // Server name.
439 "0000000000000000000000000000000000000000000000000000000000000000" +
440 "0000000000000000000000000000000000000000000000000000000000000000" +
441 // File, missing one byte
442 "0000000000000000000000000000000000000000000000000000000000000000" +
443 "0000000000000000000000000000000000000000000000000000000000000000" +
444 "0000000000000000000000000000000000000000000000000000000000000000" +
445 "00000000000000000000000000000000000000000000000000000000000000");
446
447 try {
448 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
449 } catch (DhcpPacket.ParseException expected) {
450 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
451 return;
452 }
453 fail("Dhcp packet parsing should have failed");
454 }
455
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900456 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900457 public void testBadOfferWithoutACookie() throws Exception {
458 final byte[] packet = HexDump.hexStringToByteArray(
459 // IP header.
460 "450001518d0600004011144dc0a82b01c0a82bf7" +
461 // UDP header.
462 "00430044013d9ac7" +
463 // BOOTP header.
464 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
465 // MAC address.
466 "30766ff2a90c00000000000000000000" +
467 // Server name.
468 "0000000000000000000000000000000000000000000000000000000000000000" +
469 "0000000000000000000000000000000000000000000000000000000000000000" +
470 // File.
471 "0000000000000000000000000000000000000000000000000000000000000000" +
472 "0000000000000000000000000000000000000000000000000000000000000000" +
473 "0000000000000000000000000000000000000000000000000000000000000000" +
474 "0000000000000000000000000000000000000000000000000000000000000000"
475 // No options
476 );
477
478 try {
479 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
480 } catch (DhcpPacket.ParseException expected) {
Hugo Benichi006e0612016-10-05 21:07:19 +0900481 assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode);
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900482 return;
483 }
484 fail("Dhcp packet parsing should have failed");
485 }
486
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900487 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900488 public void testOfferWithBadCookie() throws Exception {
489 final byte[] packet = HexDump.hexStringToByteArray(
490 // IP header.
491 "450001518d0600004011144dc0a82b01c0a82bf7" +
492 // UDP header.
493 "00430044013d9ac7" +
494 // BOOTP header.
495 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
496 // MAC address.
497 "30766ff2a90c00000000000000000000" +
498 // Server name.
499 "0000000000000000000000000000000000000000000000000000000000000000" +
500 "0000000000000000000000000000000000000000000000000000000000000000" +
501 // File.
502 "0000000000000000000000000000000000000000000000000000000000000000" +
503 "0000000000000000000000000000000000000000000000000000000000000000" +
504 "0000000000000000000000000000000000000000000000000000000000000000" +
505 "0000000000000000000000000000000000000000000000000000000000000000" +
506 // Bad cookie
507 "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
508 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
509
510 try {
511 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
512 } catch (DhcpPacket.ParseException expected) {
513 assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode);
514 return;
515 }
516 fail("Dhcp packet parsing should have failed");
517 }
518
519 private void assertDhcpErrorCodes(int expected, int got) {
520 assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
521 }
522
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900523 @Test
Hugo Benichi006e0612016-10-05 21:07:19 +0900524 public void testTruncatedOfferPackets() throws Exception {
525 final byte[] packet = HexDump.hexStringToByteArray(
526 // IP header.
527 "450001518d0600004011144dc0a82b01c0a82bf7" +
528 // UDP header.
529 "00430044013d9ac7" +
530 // BOOTP header.
531 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
532 // MAC address.
533 "30766ff2a90c00000000000000000000" +
534 // Server name.
535 "0000000000000000000000000000000000000000000000000000000000000000" +
536 "0000000000000000000000000000000000000000000000000000000000000000" +
537 // File.
538 "0000000000000000000000000000000000000000000000000000000000000000" +
539 "0000000000000000000000000000000000000000000000000000000000000000" +
540 "0000000000000000000000000000000000000000000000000000000000000000" +
541 "0000000000000000000000000000000000000000000000000000000000000000" +
542 // Options
543 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
544 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
545
546 for (int len = 0; len < packet.length; len++) {
547 try {
548 DhcpPacket.decodeFullPacket(packet, len, ENCAP_L3);
549 } catch (ParseException e) {
550 if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
551 fail(String.format("bad truncated packet of length %d", len));
552 }
553 }
554 }
555 }
556
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900557 @Test
Hugo Benichi006e0612016-10-05 21:07:19 +0900558 public void testRandomPackets() throws Exception {
559 final int maxRandomPacketSize = 512;
560 final Random r = new Random();
561 for (int i = 0; i < 10000; i++) {
562 byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
563 r.nextBytes(packet);
564 try {
565 DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
566 } catch (ParseException e) {
567 if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
568 fail("bad packet: " + HexDump.toHexString(packet));
569 }
570 }
571 }
572 }
573
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900574 private byte[] mtuBytes(int mtu) {
575 // 0x1a02: option 26, length 2. 0xff: no more options.
576 if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
577 throw new IllegalArgumentException(
578 String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
579 }
580 String hexString = String.format("1a02%04xff", mtu);
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900581 return HexDump.hexStringToByteArray(hexString);
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900582 }
583
584 private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
585 if (mtuBytes != null) {
586 packet.position(packet.capacity() - mtuBytes.length);
587 packet.put(mtuBytes);
588 packet.clear();
589 }
590 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
591 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
592 DhcpResults dhcpResults = offerPacket.toDhcpResults();
593 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900594 null, "192.168.144.3", "", null, 7200, false, expectedMtu, dhcpResults);
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900595 }
596
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900597 @Test
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900598 public void testMtu() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900599 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900600 // IP header.
601 "451001480000000080118849c0a89003c0a89ff7" +
602 // UDP header.
603 "004300440134dcfa" +
604 // BOOTP header.
605 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
606 // MAC address.
607 "30766ff2a90c00000000000000000000" +
608 // Server name.
609 "0000000000000000000000000000000000000000000000000000000000000000" +
610 "0000000000000000000000000000000000000000000000000000000000000000" +
611 // File.
612 "0000000000000000000000000000000000000000000000000000000000000000" +
613 "0000000000000000000000000000000000000000000000000000000000000000" +
614 "0000000000000000000000000000000000000000000000000000000000000000" +
615 "0000000000000000000000000000000000000000000000000000000000000000" +
616 // Options
617 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900618 "3a0400000e103b040000189cff00000000"));
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900619
620 checkMtu(packet, 0, null);
621 checkMtu(packet, 0, mtuBytes(1501));
622 checkMtu(packet, 1500, mtuBytes(1500));
623 checkMtu(packet, 1499, mtuBytes(1499));
624 checkMtu(packet, 1280, mtuBytes(1280));
625 checkMtu(packet, 0, mtuBytes(1279));
626 checkMtu(packet, 0, mtuBytes(576));
627 checkMtu(packet, 0, mtuBytes(68));
628 checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
629 checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
630 checkMtu(packet, 0, mtuBytes(-1));
631 }
632
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900633 @Test
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900634 public void testBadHwaddrLength() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900635 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900636 // IP header.
637 "450001518d0600004011144dc0a82b01c0a82bf7" +
638 // UDP header.
639 "00430044013d9ac7" +
640 // BOOTP header.
641 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
642 // MAC address.
643 "30766ff2a90c00000000000000000000" +
644 // Server name.
645 "0000000000000000000000000000000000000000000000000000000000000000" +
646 "0000000000000000000000000000000000000000000000000000000000000000" +
647 // File.
648 "0000000000000000000000000000000000000000000000000000000000000000" +
649 "0000000000000000000000000000000000000000000000000000000000000000" +
650 "0000000000000000000000000000000000000000000000000000000000000000" +
651 "0000000000000000000000000000000000000000000000000000000000000000" +
652 // Options
653 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900654 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900655 String expectedClientMac = "30766FF2A90C";
656
657 final int hwAddrLenOffset = 20 + 8 + 2;
658 assertEquals(6, packet.get(hwAddrLenOffset));
659
660 // Expect the expected.
661 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
662 assertNotNull(offerPacket);
663 assertEquals(6, offerPacket.getClientMac().length);
664 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
665
666 // Reduce the hardware address length and verify that it shortens the client MAC.
667 packet.flip();
668 packet.put(hwAddrLenOffset, (byte) 5);
669 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
670 assertNotNull(offerPacket);
671 assertEquals(5, offerPacket.getClientMac().length);
672 assertEquals(expectedClientMac.substring(0, 10),
673 HexDump.toHexString(offerPacket.getClientMac()));
674
675 packet.flip();
676 packet.put(hwAddrLenOffset, (byte) 3);
677 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
678 assertNotNull(offerPacket);
679 assertEquals(3, offerPacket.getClientMac().length);
680 assertEquals(expectedClientMac.substring(0, 6),
681 HexDump.toHexString(offerPacket.getClientMac()));
682
683 // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
684 // and crash, and b) hardcode it to 6.
685 packet.flip();
686 packet.put(hwAddrLenOffset, (byte) -1);
687 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
688 assertNotNull(offerPacket);
689 assertEquals(6, offerPacket.getClientMac().length);
690 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
691
692 // Set the the hardware address length to a positive invalid value (> 16) and verify that we
693 // hardcode it to 6.
694 packet.flip();
695 packet.put(hwAddrLenOffset, (byte) 17);
696 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
697 assertNotNull(offerPacket);
698 assertEquals(6, offerPacket.getClientMac().length);
699 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
700 }
701
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900702 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900703 public void testPadAndOverloadedOptionsOffer() throws Exception {
704 // A packet observed in the real world that is interesting for two reasons:
705 //
706 // 1. It uses pad bytes, which we previously didn't support correctly.
707 // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
708 // store any information in the overloaded fields).
709 //
710 // For now, we just check that it parses correctly.
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900711 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900712 // Ethernet header.
713 "b4cef6000000e80462236e300800" +
714 // IP header.
715 "4500014c00000000ff11741701010101ac119876" +
716 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
717 "004300440138ae5a" +
718 // BOOTP header.
719 "020106000fa0059f0000000000000000ac1198760000000000000000" +
720 // MAC address.
721 "b4cef600000000000000000000000000" +
722 // Server name.
723 "ff00000000000000000000000000000000000000000000000000000000000000" +
724 "0000000000000000000000000000000000000000000000000000000000000000" +
725 // File.
726 "ff00000000000000000000000000000000000000000000000000000000000000" +
727 "0000000000000000000000000000000000000000000000000000000000000000" +
728 "0000000000000000000000000000000000000000000000000000000000000000" +
729 "0000000000000000000000000000000000000000000000000000000000000000" +
730 // Options
731 "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900732 "0000000000000000000000000000000000000000000000ff000000"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900733
734 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
735 assertTrue(offerPacket instanceof DhcpOfferPacket);
736 DhcpResults dhcpResults = offerPacket.toDhcpResults();
737 assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900738 null, "1.1.1.1", "", null, 43200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900739 }
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900740
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900741 @Test
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900742 public void testBug2111() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900743 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900744 // IP header.
745 "4500014c00000000ff119beac3eaf3880a3f5d04" +
746 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
747 "0043004401387464" +
748 // BOOTP header.
749 "0201060002554812000a0000000000000a3f5d040000000000000000" +
750 // MAC address.
751 "00904c00000000000000000000000000" +
752 // Server name.
753 "0000000000000000000000000000000000000000000000000000000000000000" +
754 "0000000000000000000000000000000000000000000000000000000000000000" +
755 // File.
756 "0000000000000000000000000000000000000000000000000000000000000000" +
757 "0000000000000000000000000000000000000000000000000000000000000000" +
758 "0000000000000000000000000000000000000000000000000000000000000000" +
759 "0000000000000000000000000000000000000000000000000000000000000000" +
760 // Options.
761 "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900762 "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900763
764 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
765 assertTrue(offerPacket instanceof DhcpOfferPacket);
766 DhcpResults dhcpResults = offerPacket.toDhcpResults();
767 assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900768 "domain123.co.uk", "192.0.2.254", "", null, 49094, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900769 }
770
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900771 @Test
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900772 public void testBug2136() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900773 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900774 // Ethernet header.
775 "bcf5ac000000d0c7890000000800" +
776 // IP header.
777 "4500014c00000000ff119beac3eaf3880a3f5d04" +
778 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
779 "0043004401387574" +
780 // BOOTP header.
781 "0201060163339a3000050000000000000a209ecd0000000000000000" +
782 // MAC address.
783 "bcf5ac00000000000000000000000000" +
784 // Server name.
785 "0000000000000000000000000000000000000000000000000000000000000000" +
786 "0000000000000000000000000000000000000000000000000000000000000000" +
787 // File.
788 "0000000000000000000000000000000000000000000000000000000000000000" +
789 "0000000000000000000000000000000000000000000000000000000000000000" +
790 "0000000000000000000000000000000000000000000000000000000000000000" +
791 "0000000000000000000000000000000000000000000000000000000000000000" +
792 // Options.
793 "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900794 "0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900795
796 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
797 assertTrue(offerPacket instanceof DhcpOfferPacket);
798 assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
799 DhcpResults dhcpResults = offerPacket.toDhcpResults();
800 assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900801 "lancs.ac.uk", "10.32.255.128", "", null, 7200, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900802 }
Erik Klineb19238c2015-10-06 18:43:17 +0900803
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900804 @Test
Erik Klineb19238c2015-10-06 18:43:17 +0900805 public void testUdpServerAnySourcePort() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900806 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Erik Klineb19238c2015-10-06 18:43:17 +0900807 // Ethernet header.
808 "9cd917000000001c2e0000000800" +
809 // IP header.
810 "45a00148000040003d115087d18194fb0a0f7af2" +
811 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
812 // NOTE: The server source port is not the canonical port 67.
813 "C29F004401341268" +
814 // BOOTP header.
815 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
816 // MAC address.
817 "9cd91700000000000000000000000000" +
818 // Server name.
819 "0000000000000000000000000000000000000000000000000000000000000000" +
820 "0000000000000000000000000000000000000000000000000000000000000000" +
821 // File.
822 "0000000000000000000000000000000000000000000000000000000000000000" +
823 "0000000000000000000000000000000000000000000000000000000000000000" +
824 "0000000000000000000000000000000000000000000000000000000000000000" +
825 "0000000000000000000000000000000000000000000000000000000000000000" +
826 // Options.
827 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900828 "d18180060f0777766d2e6564751c040a0fffffff000000"));
Erik Klineb19238c2015-10-06 18:43:17 +0900829
830 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
831 assertTrue(offerPacket instanceof DhcpOfferPacket);
832 assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
833 DhcpResults dhcpResults = offerPacket.toDhcpResults();
834 assertDhcpResults("10.15.122.242/16", "10.15.200.23",
835 "209.129.128.3,209.129.148.3,209.129.128.6",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900836 "wvm.edu", "10.1.105.252", "", null, 86400, false, 0, dhcpResults);
Erik Klineb19238c2015-10-06 18:43:17 +0900837 }
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900838
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900839 @Test
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900840 public void testUdpInvalidDstPort() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900841 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900842 // Ethernet header.
843 "9cd917000000001c2e0000000800" +
844 // IP header.
845 "45a00148000040003d115087d18194fb0a0f7af2" +
846 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
847 // NOTE: The destination port is a non-DHCP port.
848 "0043aaaa01341268" +
849 // BOOTP header.
850 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
851 // MAC address.
852 "9cd91700000000000000000000000000" +
853 // Server name.
854 "0000000000000000000000000000000000000000000000000000000000000000" +
855 "0000000000000000000000000000000000000000000000000000000000000000" +
856 // File.
857 "0000000000000000000000000000000000000000000000000000000000000000" +
858 "0000000000000000000000000000000000000000000000000000000000000000" +
859 "0000000000000000000000000000000000000000000000000000000000000000" +
860 "0000000000000000000000000000000000000000000000000000000000000000" +
861 // Options.
862 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900863 "d18180060f0777766d2e6564751c040a0fffffff000000"));
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900864
865 try {
866 DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
867 fail("Packet with invalid dst port did not throw ParseException");
868 } catch (ParseException expected) {}
869 }
870
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900871 @Test
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900872 public void testMultipleRouters() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900873 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900874 // Ethernet header.
875 "fc3d93000000" + "081735000000" + "0800" +
876 // IP header.
877 "45000148c2370000ff117ac2c0a8bd02ffffffff" +
878 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
879 "0043004401343beb" +
880 // BOOTP header.
881 "0201060027f518e20000800000000000c0a8bd310000000000000000" +
882 // MAC address.
883 "fc3d9300000000000000000000000000" +
884 // Server name.
885 "0000000000000000000000000000000000000000000000000000000000000000" +
886 "0000000000000000000000000000000000000000000000000000000000000000" +
887 // File.
888 "0000000000000000000000000000000000000000000000000000000000000000" +
889 "0000000000000000000000000000000000000000000000000000000000000000" +
890 "0000000000000000000000000000000000000000000000000000000000000000" +
891 "0000000000000000000000000000000000000000000000000000000000000000" +
892 // Options.
893 "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900894 "0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900895
896 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
897 assertTrue(offerPacket instanceof DhcpOfferPacket);
898 assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
899 DhcpResults dhcpResults = offerPacket.toDhcpResults();
900 assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
Lorenzo Colitti4d92d762019-04-26 19:10:20 +0900901 null, "192.171.189.2", "", null, 28800, false, 0, dhcpResults);
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900902 }
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900903
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900904 @Test
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900905 public void testDiscoverPacket() throws Exception {
906 short secs = 7;
907 int transactionId = 0xdeadbeef;
908 byte[] hwaddr = {
909 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
910 };
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900911
912 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
913 DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
Erik Kline1d511ab2016-03-24 17:30:13 +0900914 false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900915
916 byte[] headers = new byte[] {
917 // Ethernet header.
918 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
919 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
920 (byte) 0x08, (byte) 0x00,
921 // IP header.
Erik Kline1d511ab2016-03-24 17:30:13 +0900922 (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900923 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
Erik Kline1d511ab2016-03-24 17:30:13 +0900924 (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900925 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
926 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
927 // UDP header.
928 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
Erik Kline1d511ab2016-03-24 17:30:13 +0900929 (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900930 // BOOTP.
931 (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
932 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
933 (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
934 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
935 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
936 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
937 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
938 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
939 (byte) 0xb1, (byte) 0x7a
940 };
941 byte[] options = new byte[] {
942 // Magic cookie 0x63825363.
943 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
944 // Message type DISCOVER.
945 (byte) 0x35, (byte) 0x01, (byte) 0x01,
946 // Client identifier Ethernet, da:01:19:5b:b1:7a.
947 (byte) 0x3d, (byte) 0x07,
948 (byte) 0x01,
949 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
950 // Max message size 1500.
951 (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
952 // Version "android-dhcp-???".
953 (byte) 0x3c, (byte) 0x10,
954 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
955 // Hostname "android-01234567890abcde"
956 (byte) 0x0c, (byte) 0x18,
957 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
958 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
959 // Requested parameter list.
Erik Kline1d511ab2016-03-24 17:30:13 +0900960 (byte) 0x37, (byte) 0x0a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900961 DHCP_SUBNET_MASK,
962 DHCP_ROUTER,
963 DHCP_DNS_SERVER,
964 DHCP_DOMAIN_NAME,
965 DHCP_MTU,
Erik Kline1d511ab2016-03-24 17:30:13 +0900966 DHCP_BROADCAST_ADDRESS,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900967 DHCP_LEASE_TIME,
Erik Kline1d511ab2016-03-24 17:30:13 +0900968 DHCP_RENEWAL_TIME,
969 DHCP_REBINDING_TIME,
970 DHCP_VENDOR_INFO,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900971 // End options.
972 (byte) 0xff,
973 // Our packets are always of even length. TODO: find out why and possibly fix it.
974 (byte) 0x00
975 };
976 byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
977 assertTrue((expected.length & 1) == 0);
978 System.arraycopy(headers, 0, expected, 0, headers.length);
979 System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
980
981 byte[] actual = new byte[packet.limit()];
982 packet.get(actual);
983 String msg =
984 "Expected:\n " + Arrays.toString(expected) +
985 "\nActual:\n " + Arrays.toString(actual);
986 assertTrue(msg, Arrays.equals(expected, actual));
987 }
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900988
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +0900989 public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
990 throws Exception {
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900991 final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
992 final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
993 final int transactionId = 0xdeadbeef;
994
995 final ByteBuffer packet = DhcpPacket.buildOfferPacket(
996 DhcpPacket.ENCAP_BOOTP, transactionId, false /* broadcast */,
997 SERVER_ADDR, INADDR_ANY /* relayIp */, CLIENT_ADDR /* yourIp */,
998 CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
999 BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
1000 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001001 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
1002 false /* metered */, MTU);
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001003
1004 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1005 // BOOTP headers
1006 bos.write(new byte[] {
1007 (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x00,
1008 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
1009 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
1010 // ciaddr
1011 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
1012 });
1013 // yiaddr
1014 bos.write(CLIENT_ADDR.getAddress());
1015 // siaddr
1016 bos.write(SERVER_ADDR.getAddress());
1017 // giaddr
1018 bos.write(INADDR_ANY.getAddress());
1019 // chaddr
1020 bos.write(CLIENT_MAC);
1021
1022 // Padding
1023 bos.write(new byte[202]);
1024
1025 // Options
1026 bos.write(new byte[]{
1027 // Magic cookie 0x63825363.
1028 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
1029 // Message type OFFER.
1030 (byte) 0x35, (byte) 0x01, (byte) 0x02,
1031 });
1032 // Server ID
1033 bos.write(new byte[] { (byte) 0x36, (byte) 0x04 });
1034 bos.write(SERVER_ADDR.getAddress());
1035 // Lease time
1036 bos.write(new byte[] { (byte) 0x33, (byte) 0x04 });
1037 bos.write(intToByteArray(leaseTimeSecs));
1038 if (leaseTimeSecs != INFINITE_LEASE) {
1039 // Renewal time
1040 bos.write(new byte[]{(byte) 0x3a, (byte) 0x04});
1041 bos.write(intToByteArray(renewalTime));
1042 // Rebinding time
1043 bos.write(new byte[]{(byte) 0x3b, (byte) 0x04});
1044 bos.write(intToByteArray(rebindingTime));
1045 }
1046 // Subnet mask
1047 bos.write(new byte[] { (byte) 0x01, (byte) 0x04 });
1048 bos.write(NETMASK.getAddress());
1049 // Broadcast address
1050 bos.write(new byte[] { (byte) 0x1c, (byte) 0x04 });
1051 bos.write(BROADCAST_ADDR.getAddress());
1052 // Router
1053 bos.write(new byte[] { (byte) 0x03, (byte) 0x04 });
1054 bos.write(SERVER_ADDR.getAddress());
1055 // Nameserver
1056 bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
1057 bos.write(SERVER_ADDR.getAddress());
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001058 // Hostname
1059 if (hostname != null) {
1060 bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
1061 bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
1062 }
1063 // MTU
1064 bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
1065 bos.write(shortToByteArray(MTU));
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001066 // End options.
1067 bos.write(0xff);
1068
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001069 if ((bos.size() & 1) != 0) {
1070 bos.write(0x00);
1071 }
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001072
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001073 final byte[] expected = bos.toByteArray();
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001074 final byte[] actual = new byte[packet.limit()];
1075 packet.get(actual);
1076 final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
1077 "\nActual:\n " + HexDump.dumpHexString(actual);
1078 assertTrue(msg, Arrays.equals(expected, actual));
1079 }
1080
1081 @Test
1082 public void testOfferPacket() throws Exception {
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001083 checkBuildOfferPacket(3600, HOSTNAME);
1084 checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
1085 checkBuildOfferPacket(0x80000000, HOSTNAME);
1086 checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
1087 checkBuildOfferPacket(3600, null);
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001088 }
1089
1090 private static byte[] intToByteArray(int val) {
1091 return ByteBuffer.allocate(4).putInt(val).array();
1092 }
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001093
1094 private static byte[] shortToByteArray(short val) {
1095 return ByteBuffer.allocate(2).putShort(val).array();
1096 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +09001097}