blob: 7544e72da02eac1068a7723515dd328143f4e51d [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;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090051import android.support.test.filters.SmallTest;
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090052import android.support.test.runner.AndroidJUnit4;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090053
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090054import com.android.internal.util.HexDump;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090055
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090056import org.junit.Before;
57import org.junit.Test;
58import org.junit.runner.RunWith;
59
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090060import java.io.ByteArrayOutputStream;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090061import java.net.Inet4Address;
62import java.nio.ByteBuffer;
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090063import java.nio.charset.Charset;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090064import java.util.ArrayList;
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090065import java.util.Arrays;
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090066import java.util.Collections;
Hugo Benichi006e0612016-10-05 21:07:19 +090067import java.util.Random;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090068
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090069@RunWith(AndroidJUnit4.class)
70@SmallTest
71public class DhcpPacketTest {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090072
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090073 private static final Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
74 private static final Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
75 private static final int PREFIX_LENGTH = 22;
76 private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
77 private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
78 SERVER_ADDR, PREFIX_LENGTH);
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090079 private static final String HOSTNAME = "testhostname";
80 private static final short MTU = 1500;
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090081 // 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 +090082 // doesn't use == instead of equals when comparing addresses.
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090083 private static final Inet4Address ANY = v4Address("0.0.0.0");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090084
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090085 private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090086
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090087 private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
88 return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
89 }
90
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090091 @Before
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090092 public void setUp() {
93 DhcpPacket.testOverrideVendorId = "android-dhcp-???";
94 DhcpPacket.testOverrideHostname = "android-01234567890abcde";
95 }
96
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090097 class TestDhcpPacket extends DhcpPacket {
98 private byte mType;
99 // TODO: Make this a map of option numbers to bytes instead.
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900100 private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900101
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900102 public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
103 super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900104 CLIENT_MAC, true);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900105 mType = type;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900106 }
107
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900108 public TestDhcpPacket(byte type) {
109 this(type, INADDR_ANY, CLIENT_ADDR);
110 }
111
Lorenzo Colittid9735372015-06-02 13:15:50 +0900112 public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900113 mDomainBytes = domainBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900114 return this;
115 }
116
117 public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900118 mVendorInfoBytes = vendorInfoBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900119 return this;
120 }
121
122 public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
123 mLeaseTimeBytes = leaseTimeBytes;
124 return this;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900125 }
126
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900127 public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
128 mNetmaskBytes = netmaskBytes;
129 return this;
130 }
131
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900132 public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
133 ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
134 fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
135 DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
136 return result;
137 }
138
139 public void finishPacket(ByteBuffer buffer) {
140 addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
141 if (mDomainBytes != null) {
142 addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
143 }
144 if (mVendorInfoBytes != null) {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900145 addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900146 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900147 if (mLeaseTimeBytes != null) {
148 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
149 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900150 if (mNetmaskBytes != null) {
151 addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
152 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900153 addTlvEnd(buffer);
154 }
155
156 // Convenience method.
157 public ByteBuffer build() {
158 // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
159 ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
160 pkt.flip();
161 return pkt;
162 }
163 }
164
165 private void assertDomainAndVendorInfoParses(
166 String expectedDomain, byte[] domainBytes,
Erik Kline496906e2015-09-16 15:44:54 +0900167 String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900168 ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
169 .setDomainBytes(domainBytes)
170 .setVendorInfoBytes(vendorInfoBytes)
171 .build();
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900172 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
173 assertEquals(expectedDomain, offerPacket.mDomainName);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900174 assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900175 }
176
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900177 @Test
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900178 public void testDomainName() throws Exception {
179 byte[] nullByte = new byte[] { 0x00 };
180 byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
181 byte[] nonNullDomain = new byte[] {
182 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
183 };
184 byte[] trailingNullDomain = new byte[] {
185 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
186 };
187 byte[] embeddedNullsDomain = new byte[] {
188 (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
189 };
190 byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
191
192 byte[] meteredEmbeddedNull = metered.clone();
193 meteredEmbeddedNull[7] = (char) 0;
194
195 byte[] meteredTrailingNull = metered.clone();
196 meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
197
198 assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
199 assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
200 assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
201 assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
202 "ANDROID\u0000METERED", meteredEmbeddedNull);
203 assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
204 "ANDROID_METERE\u0000", meteredTrailingNull);
205 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900206
207 private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
Erik Kline496906e2015-09-16 15:44:54 +0900208 long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900209 TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
210 if (leaseTimeBytes != null) {
211 testPacket.setLeaseTimeBytes(leaseTimeBytes);
212 }
213 ByteBuffer packet = testPacket.build();
Erik Kline496906e2015-09-16 15:44:54 +0900214 DhcpPacket offerPacket = null;
215
Lorenzo Colittid9735372015-06-02 13:15:50 +0900216 if (!expectValid) {
Erik Kline496906e2015-09-16 15:44:54 +0900217 try {
218 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
219 fail("Invalid packet parsed successfully: " + offerPacket);
220 } catch (ParseException expected) {
221 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900222 return;
223 }
Erik Kline496906e2015-09-16 15:44:54 +0900224
225 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
226 assertNotNull(offerPacket);
Lorenzo Colittid9735372015-06-02 13:15:50 +0900227 assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
228 DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
229 assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
230 }
231
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900232 @Test
Lorenzo Colittid9735372015-06-02 13:15:50 +0900233 public void testLeaseTime() throws Exception {
234 byte[] noLease = null;
235 byte[] tooShortLease = new byte[] { 0x00, 0x00 };
236 byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
237 byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
238 byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
239 byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
240 byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
241 byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
242 byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
243 byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
244
245 assertLeaseTimeParses(true, null, 0, noLease);
246 assertLeaseTimeParses(false, null, 0, tooShortLease);
247 assertLeaseTimeParses(false, null, 0, tooLongLease);
248 assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
249 assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
250 assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
251 assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
252 assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
253 assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
254 assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
255 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900256
257 private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900258 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900259 checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
260 checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
261 }
262
263 private void checkIpAddress(String expected, byte type,
264 Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900265 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900266 ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
267 .setNetmaskBytes(netmaskBytes)
268 .build();
269 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
270 DhcpResults results = offerPacket.toDhcpResults();
271
272 if (expected != null) {
273 LinkAddress expectedAddress = new LinkAddress(expected);
274 assertEquals(expectedAddress, results.ipAddress);
275 } else {
276 assertNull(results);
277 }
278 }
279
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900280 @Test
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900281 public void testIpAddress() throws Exception {
282 byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
283 byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
284 byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900285 Inet4Address example1 = v4Address("192.0.2.1");
286 Inet4Address example2 = v4Address("192.0.2.43");
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900287
288 // A packet without any addresses is not valid.
289 checkIpAddress(null, ANY, ANY, slash24Netmask);
290
291 // ClientIP is used iff YourIP is not present.
292 checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
293 checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
294 checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
295
296 // Invalid netmasks are ignored.
297 checkIpAddress(null, example2, ANY, invalidNetmask);
298
299 // If there is no netmask, implicit netmasks are used.
300 checkIpAddress("192.0.2.43/24", ANY, example2, null);
301 }
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900302
303 private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
304 String domains, String serverAddress, String vendorInfo, int leaseDuration,
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900305 boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900306 assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
307 assertEquals(v4Address(gateway), dhcpResults.gateway);
308
309 String[] dnsServerStrings = dnsServersString.split(",");
310 ArrayList dnsServers = new ArrayList();
311 for (String dnsServerString : dnsServerStrings) {
312 dnsServers.add(v4Address(dnsServerString));
313 }
314 assertEquals(dnsServers, dhcpResults.dnsServers);
315
316 assertEquals(domains, dhcpResults.domains);
317 assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
318 assertEquals(vendorInfo, dhcpResults.vendorInfo);
319 assertEquals(leaseDuration, dhcpResults.leaseDuration);
320 assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900321 assertEquals(mtu, dhcpResults.mtu);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900322 }
323
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900324 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900325 public void testOffer1() throws Exception {
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900326 // TODO: Turn all of these into golden files. This will probably require using
327 // android.support.test.InstrumentationRegistry for obtaining a Context object
328 // to read such golden files, along with an appropriate Android.mk.
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900329 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900330 // IP header.
331 "451001480000000080118849c0a89003c0a89ff7" +
332 // UDP header.
333 "004300440134dcfa" +
334 // BOOTP header.
335 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
336 // MAC address.
337 "30766ff2a90c00000000000000000000" +
338 // Server name.
339 "0000000000000000000000000000000000000000000000000000000000000000" +
340 "0000000000000000000000000000000000000000000000000000000000000000" +
341 // File.
342 "0000000000000000000000000000000000000000000000000000000000000000" +
343 "0000000000000000000000000000000000000000000000000000000000000000" +
344 "0000000000000000000000000000000000000000000000000000000000000000" +
345 "0000000000000000000000000000000000000000000000000000000000000000" +
346 // Options
347 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900348 "3a0400000e103b040000189cff00000000000000000000"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900349
350 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
351 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
352 DhcpResults dhcpResults = offerPacket.toDhcpResults();
353 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900354 null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900355 }
356
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900357 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900358 public void testOffer2() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900359 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900360 // IP header.
361 "450001518d0600004011144dc0a82b01c0a82bf7" +
362 // UDP header.
363 "00430044013d9ac7" +
364 // BOOTP header.
365 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
366 // MAC address.
367 "30766ff2a90c00000000000000000000" +
368 // Server name.
369 "0000000000000000000000000000000000000000000000000000000000000000" +
370 "0000000000000000000000000000000000000000000000000000000000000000" +
371 // File.
372 "0000000000000000000000000000000000000000000000000000000000000000" +
373 "0000000000000000000000000000000000000000000000000000000000000000" +
374 "0000000000000000000000000000000000000000000000000000000000000000" +
375 "0000000000000000000000000000000000000000000000000000000000000000" +
376 // Options
377 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900378 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900379
380 assertEquals(337, packet.limit());
381 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
382 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
383 DhcpResults dhcpResults = offerPacket.toDhcpResults();
384 assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900385 null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900386 assertTrue(dhcpResults.hasMeteredHint());
387 }
388
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900389 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900390 public void testBadIpPacket() throws Exception {
391 final byte[] packet = HexDump.hexStringToByteArray(
392 // IP header.
393 "450001518d0600004011144dc0a82b01c0a82bf7");
394
395 try {
396 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
397 } catch (DhcpPacket.ParseException expected) {
398 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
399 return;
400 }
401 fail("Dhcp packet parsing should have failed");
402 }
403
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900404 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900405 public void testBadDhcpPacket() throws Exception {
406 final byte[] packet = HexDump.hexStringToByteArray(
407 // IP header.
408 "450001518d0600004011144dc0a82b01c0a82bf7" +
409 // UDP header.
410 "00430044013d9ac7" +
411 // BOOTP header.
412 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
413
414 try {
415 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
416 } catch (DhcpPacket.ParseException expected) {
417 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
418 return;
419 }
420 fail("Dhcp packet parsing should have failed");
421 }
422
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900423 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900424 public void testBadTruncatedOffer() throws Exception {
425 final byte[] packet = HexDump.hexStringToByteArray(
426 // IP header.
427 "450001518d0600004011144dc0a82b01c0a82bf7" +
428 // UDP header.
429 "00430044013d9ac7" +
430 // BOOTP header.
431 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
432 // MAC address.
433 "30766ff2a90c00000000000000000000" +
434 // Server name.
435 "0000000000000000000000000000000000000000000000000000000000000000" +
436 "0000000000000000000000000000000000000000000000000000000000000000" +
437 // File, missing one byte
438 "0000000000000000000000000000000000000000000000000000000000000000" +
439 "0000000000000000000000000000000000000000000000000000000000000000" +
440 "0000000000000000000000000000000000000000000000000000000000000000" +
441 "00000000000000000000000000000000000000000000000000000000000000");
442
443 try {
444 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
445 } catch (DhcpPacket.ParseException expected) {
446 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
447 return;
448 }
449 fail("Dhcp packet parsing should have failed");
450 }
451
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900452 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900453 public void testBadOfferWithoutACookie() throws Exception {
454 final byte[] packet = HexDump.hexStringToByteArray(
455 // IP header.
456 "450001518d0600004011144dc0a82b01c0a82bf7" +
457 // UDP header.
458 "00430044013d9ac7" +
459 // BOOTP header.
460 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
461 // MAC address.
462 "30766ff2a90c00000000000000000000" +
463 // Server name.
464 "0000000000000000000000000000000000000000000000000000000000000000" +
465 "0000000000000000000000000000000000000000000000000000000000000000" +
466 // File.
467 "0000000000000000000000000000000000000000000000000000000000000000" +
468 "0000000000000000000000000000000000000000000000000000000000000000" +
469 "0000000000000000000000000000000000000000000000000000000000000000" +
470 "0000000000000000000000000000000000000000000000000000000000000000"
471 // No options
472 );
473
474 try {
475 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
476 } catch (DhcpPacket.ParseException expected) {
Hugo Benichi006e0612016-10-05 21:07:19 +0900477 assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode);
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900478 return;
479 }
480 fail("Dhcp packet parsing should have failed");
481 }
482
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900483 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900484 public void testOfferWithBadCookie() throws Exception {
485 final byte[] packet = HexDump.hexStringToByteArray(
486 // IP header.
487 "450001518d0600004011144dc0a82b01c0a82bf7" +
488 // UDP header.
489 "00430044013d9ac7" +
490 // BOOTP header.
491 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
492 // MAC address.
493 "30766ff2a90c00000000000000000000" +
494 // Server name.
495 "0000000000000000000000000000000000000000000000000000000000000000" +
496 "0000000000000000000000000000000000000000000000000000000000000000" +
497 // File.
498 "0000000000000000000000000000000000000000000000000000000000000000" +
499 "0000000000000000000000000000000000000000000000000000000000000000" +
500 "0000000000000000000000000000000000000000000000000000000000000000" +
501 "0000000000000000000000000000000000000000000000000000000000000000" +
502 // Bad cookie
503 "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
504 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
505
506 try {
507 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
508 } catch (DhcpPacket.ParseException expected) {
509 assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode);
510 return;
511 }
512 fail("Dhcp packet parsing should have failed");
513 }
514
515 private void assertDhcpErrorCodes(int expected, int got) {
516 assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
517 }
518
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900519 @Test
Hugo Benichi006e0612016-10-05 21:07:19 +0900520 public void testTruncatedOfferPackets() throws Exception {
521 final byte[] packet = HexDump.hexStringToByteArray(
522 // IP header.
523 "450001518d0600004011144dc0a82b01c0a82bf7" +
524 // UDP header.
525 "00430044013d9ac7" +
526 // BOOTP header.
527 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
528 // MAC address.
529 "30766ff2a90c00000000000000000000" +
530 // Server name.
531 "0000000000000000000000000000000000000000000000000000000000000000" +
532 "0000000000000000000000000000000000000000000000000000000000000000" +
533 // File.
534 "0000000000000000000000000000000000000000000000000000000000000000" +
535 "0000000000000000000000000000000000000000000000000000000000000000" +
536 "0000000000000000000000000000000000000000000000000000000000000000" +
537 "0000000000000000000000000000000000000000000000000000000000000000" +
538 // Options
539 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
540 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
541
542 for (int len = 0; len < packet.length; len++) {
543 try {
544 DhcpPacket.decodeFullPacket(packet, len, ENCAP_L3);
545 } catch (ParseException e) {
546 if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
547 fail(String.format("bad truncated packet of length %d", len));
548 }
549 }
550 }
551 }
552
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900553 @Test
Hugo Benichi006e0612016-10-05 21:07:19 +0900554 public void testRandomPackets() throws Exception {
555 final int maxRandomPacketSize = 512;
556 final Random r = new Random();
557 for (int i = 0; i < 10000; i++) {
558 byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
559 r.nextBytes(packet);
560 try {
561 DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
562 } catch (ParseException e) {
563 if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
564 fail("bad packet: " + HexDump.toHexString(packet));
565 }
566 }
567 }
568 }
569
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900570 private byte[] mtuBytes(int mtu) {
571 // 0x1a02: option 26, length 2. 0xff: no more options.
572 if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
573 throw new IllegalArgumentException(
574 String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
575 }
576 String hexString = String.format("1a02%04xff", mtu);
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900577 return HexDump.hexStringToByteArray(hexString);
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900578 }
579
580 private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
581 if (mtuBytes != null) {
582 packet.position(packet.capacity() - mtuBytes.length);
583 packet.put(mtuBytes);
584 packet.clear();
585 }
586 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
587 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
588 DhcpResults dhcpResults = offerPacket.toDhcpResults();
589 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
590 null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
591 }
592
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900593 @Test
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900594 public void testMtu() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900595 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900596 // IP header.
597 "451001480000000080118849c0a89003c0a89ff7" +
598 // UDP header.
599 "004300440134dcfa" +
600 // BOOTP header.
601 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
602 // MAC address.
603 "30766ff2a90c00000000000000000000" +
604 // Server name.
605 "0000000000000000000000000000000000000000000000000000000000000000" +
606 "0000000000000000000000000000000000000000000000000000000000000000" +
607 // File.
608 "0000000000000000000000000000000000000000000000000000000000000000" +
609 "0000000000000000000000000000000000000000000000000000000000000000" +
610 "0000000000000000000000000000000000000000000000000000000000000000" +
611 "0000000000000000000000000000000000000000000000000000000000000000" +
612 // Options
613 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900614 "3a0400000e103b040000189cff00000000"));
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900615
616 checkMtu(packet, 0, null);
617 checkMtu(packet, 0, mtuBytes(1501));
618 checkMtu(packet, 1500, mtuBytes(1500));
619 checkMtu(packet, 1499, mtuBytes(1499));
620 checkMtu(packet, 1280, mtuBytes(1280));
621 checkMtu(packet, 0, mtuBytes(1279));
622 checkMtu(packet, 0, mtuBytes(576));
623 checkMtu(packet, 0, mtuBytes(68));
624 checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
625 checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
626 checkMtu(packet, 0, mtuBytes(-1));
627 }
628
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900629 @Test
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900630 public void testBadHwaddrLength() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900631 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900632 // IP header.
633 "450001518d0600004011144dc0a82b01c0a82bf7" +
634 // UDP header.
635 "00430044013d9ac7" +
636 // BOOTP header.
637 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
638 // MAC address.
639 "30766ff2a90c00000000000000000000" +
640 // Server name.
641 "0000000000000000000000000000000000000000000000000000000000000000" +
642 "0000000000000000000000000000000000000000000000000000000000000000" +
643 // File.
644 "0000000000000000000000000000000000000000000000000000000000000000" +
645 "0000000000000000000000000000000000000000000000000000000000000000" +
646 "0000000000000000000000000000000000000000000000000000000000000000" +
647 "0000000000000000000000000000000000000000000000000000000000000000" +
648 // Options
649 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900650 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900651 String expectedClientMac = "30766FF2A90C";
652
653 final int hwAddrLenOffset = 20 + 8 + 2;
654 assertEquals(6, packet.get(hwAddrLenOffset));
655
656 // Expect the expected.
657 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
658 assertNotNull(offerPacket);
659 assertEquals(6, offerPacket.getClientMac().length);
660 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
661
662 // Reduce the hardware address length and verify that it shortens the client MAC.
663 packet.flip();
664 packet.put(hwAddrLenOffset, (byte) 5);
665 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
666 assertNotNull(offerPacket);
667 assertEquals(5, offerPacket.getClientMac().length);
668 assertEquals(expectedClientMac.substring(0, 10),
669 HexDump.toHexString(offerPacket.getClientMac()));
670
671 packet.flip();
672 packet.put(hwAddrLenOffset, (byte) 3);
673 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
674 assertNotNull(offerPacket);
675 assertEquals(3, offerPacket.getClientMac().length);
676 assertEquals(expectedClientMac.substring(0, 6),
677 HexDump.toHexString(offerPacket.getClientMac()));
678
679 // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
680 // and crash, and b) hardcode it to 6.
681 packet.flip();
682 packet.put(hwAddrLenOffset, (byte) -1);
683 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
684 assertNotNull(offerPacket);
685 assertEquals(6, offerPacket.getClientMac().length);
686 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
687
688 // Set the the hardware address length to a positive invalid value (> 16) and verify that we
689 // hardcode it to 6.
690 packet.flip();
691 packet.put(hwAddrLenOffset, (byte) 17);
692 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
693 assertNotNull(offerPacket);
694 assertEquals(6, offerPacket.getClientMac().length);
695 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
696 }
697
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900698 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900699 public void testPadAndOverloadedOptionsOffer() throws Exception {
700 // A packet observed in the real world that is interesting for two reasons:
701 //
702 // 1. It uses pad bytes, which we previously didn't support correctly.
703 // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
704 // store any information in the overloaded fields).
705 //
706 // For now, we just check that it parses correctly.
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900707 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900708 // Ethernet header.
709 "b4cef6000000e80462236e300800" +
710 // IP header.
711 "4500014c00000000ff11741701010101ac119876" +
712 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
713 "004300440138ae5a" +
714 // BOOTP header.
715 "020106000fa0059f0000000000000000ac1198760000000000000000" +
716 // MAC address.
717 "b4cef600000000000000000000000000" +
718 // Server name.
719 "ff00000000000000000000000000000000000000000000000000000000000000" +
720 "0000000000000000000000000000000000000000000000000000000000000000" +
721 // File.
722 "ff00000000000000000000000000000000000000000000000000000000000000" +
723 "0000000000000000000000000000000000000000000000000000000000000000" +
724 "0000000000000000000000000000000000000000000000000000000000000000" +
725 "0000000000000000000000000000000000000000000000000000000000000000" +
726 // Options
727 "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900728 "0000000000000000000000000000000000000000000000ff000000"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900729
730 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
731 assertTrue(offerPacket instanceof DhcpOfferPacket);
732 DhcpResults dhcpResults = offerPacket.toDhcpResults();
733 assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900734 null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900735 }
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900736
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900737 @Test
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900738 public void testBug2111() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900739 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900740 // IP header.
741 "4500014c00000000ff119beac3eaf3880a3f5d04" +
742 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
743 "0043004401387464" +
744 // BOOTP header.
745 "0201060002554812000a0000000000000a3f5d040000000000000000" +
746 // MAC address.
747 "00904c00000000000000000000000000" +
748 // Server name.
749 "0000000000000000000000000000000000000000000000000000000000000000" +
750 "0000000000000000000000000000000000000000000000000000000000000000" +
751 // File.
752 "0000000000000000000000000000000000000000000000000000000000000000" +
753 "0000000000000000000000000000000000000000000000000000000000000000" +
754 "0000000000000000000000000000000000000000000000000000000000000000" +
755 "0000000000000000000000000000000000000000000000000000000000000000" +
756 // Options.
757 "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900758 "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900759
760 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
761 assertTrue(offerPacket instanceof DhcpOfferPacket);
762 DhcpResults dhcpResults = offerPacket.toDhcpResults();
763 assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900764 "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900765 }
766
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900767 @Test
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900768 public void testBug2136() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900769 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900770 // Ethernet header.
771 "bcf5ac000000d0c7890000000800" +
772 // IP header.
773 "4500014c00000000ff119beac3eaf3880a3f5d04" +
774 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
775 "0043004401387574" +
776 // BOOTP header.
777 "0201060163339a3000050000000000000a209ecd0000000000000000" +
778 // MAC address.
779 "bcf5ac00000000000000000000000000" +
780 // Server name.
781 "0000000000000000000000000000000000000000000000000000000000000000" +
782 "0000000000000000000000000000000000000000000000000000000000000000" +
783 // File.
784 "0000000000000000000000000000000000000000000000000000000000000000" +
785 "0000000000000000000000000000000000000000000000000000000000000000" +
786 "0000000000000000000000000000000000000000000000000000000000000000" +
787 "0000000000000000000000000000000000000000000000000000000000000000" +
788 // Options.
789 "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900790 "0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900791
792 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
793 assertTrue(offerPacket instanceof DhcpOfferPacket);
794 assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
795 DhcpResults dhcpResults = offerPacket.toDhcpResults();
796 assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900797 "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900798 }
Erik Klineb19238c2015-10-06 18:43:17 +0900799
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900800 @Test
Erik Klineb19238c2015-10-06 18:43:17 +0900801 public void testUdpServerAnySourcePort() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900802 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Erik Klineb19238c2015-10-06 18:43:17 +0900803 // Ethernet header.
804 "9cd917000000001c2e0000000800" +
805 // IP header.
806 "45a00148000040003d115087d18194fb0a0f7af2" +
807 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
808 // NOTE: The server source port is not the canonical port 67.
809 "C29F004401341268" +
810 // BOOTP header.
811 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
812 // MAC address.
813 "9cd91700000000000000000000000000" +
814 // Server name.
815 "0000000000000000000000000000000000000000000000000000000000000000" +
816 "0000000000000000000000000000000000000000000000000000000000000000" +
817 // File.
818 "0000000000000000000000000000000000000000000000000000000000000000" +
819 "0000000000000000000000000000000000000000000000000000000000000000" +
820 "0000000000000000000000000000000000000000000000000000000000000000" +
821 "0000000000000000000000000000000000000000000000000000000000000000" +
822 // Options.
823 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900824 "d18180060f0777766d2e6564751c040a0fffffff000000"));
Erik Klineb19238c2015-10-06 18:43:17 +0900825
826 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
827 assertTrue(offerPacket instanceof DhcpOfferPacket);
828 assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
829 DhcpResults dhcpResults = offerPacket.toDhcpResults();
830 assertDhcpResults("10.15.122.242/16", "10.15.200.23",
831 "209.129.128.3,209.129.148.3,209.129.128.6",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900832 "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
Erik Klineb19238c2015-10-06 18:43:17 +0900833 }
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900834
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900835 @Test
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900836 public void testUdpInvalidDstPort() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900837 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900838 // Ethernet header.
839 "9cd917000000001c2e0000000800" +
840 // IP header.
841 "45a00148000040003d115087d18194fb0a0f7af2" +
842 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
843 // NOTE: The destination port is a non-DHCP port.
844 "0043aaaa01341268" +
845 // BOOTP header.
846 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
847 // MAC address.
848 "9cd91700000000000000000000000000" +
849 // Server name.
850 "0000000000000000000000000000000000000000000000000000000000000000" +
851 "0000000000000000000000000000000000000000000000000000000000000000" +
852 // File.
853 "0000000000000000000000000000000000000000000000000000000000000000" +
854 "0000000000000000000000000000000000000000000000000000000000000000" +
855 "0000000000000000000000000000000000000000000000000000000000000000" +
856 "0000000000000000000000000000000000000000000000000000000000000000" +
857 // Options.
858 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900859 "d18180060f0777766d2e6564751c040a0fffffff000000"));
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900860
861 try {
862 DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
863 fail("Packet with invalid dst port did not throw ParseException");
864 } catch (ParseException expected) {}
865 }
866
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900867 @Test
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900868 public void testMultipleRouters() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900869 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900870 // Ethernet header.
871 "fc3d93000000" + "081735000000" + "0800" +
872 // IP header.
873 "45000148c2370000ff117ac2c0a8bd02ffffffff" +
874 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
875 "0043004401343beb" +
876 // BOOTP header.
877 "0201060027f518e20000800000000000c0a8bd310000000000000000" +
878 // MAC address.
879 "fc3d9300000000000000000000000000" +
880 // Server name.
881 "0000000000000000000000000000000000000000000000000000000000000000" +
882 "0000000000000000000000000000000000000000000000000000000000000000" +
883 // File.
884 "0000000000000000000000000000000000000000000000000000000000000000" +
885 "0000000000000000000000000000000000000000000000000000000000000000" +
886 "0000000000000000000000000000000000000000000000000000000000000000" +
887 "0000000000000000000000000000000000000000000000000000000000000000" +
888 // Options.
889 "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900890 "0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900891
892 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
893 assertTrue(offerPacket instanceof DhcpOfferPacket);
894 assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
895 DhcpResults dhcpResults = offerPacket.toDhcpResults();
896 assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900897 null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900898 }
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900899
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900900 @Test
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900901 public void testDiscoverPacket() throws Exception {
902 short secs = 7;
903 int transactionId = 0xdeadbeef;
904 byte[] hwaddr = {
905 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
906 };
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900907
908 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
909 DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
Erik Kline1d511ab2016-03-24 17:30:13 +0900910 false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900911
912 byte[] headers = new byte[] {
913 // Ethernet header.
914 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
915 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
916 (byte) 0x08, (byte) 0x00,
917 // IP header.
Erik Kline1d511ab2016-03-24 17:30:13 +0900918 (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900919 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
Erik Kline1d511ab2016-03-24 17:30:13 +0900920 (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900921 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
922 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
923 // UDP header.
924 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
Erik Kline1d511ab2016-03-24 17:30:13 +0900925 (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900926 // BOOTP.
927 (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
928 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
929 (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
930 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
931 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
932 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
933 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
934 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
935 (byte) 0xb1, (byte) 0x7a
936 };
937 byte[] options = new byte[] {
938 // Magic cookie 0x63825363.
939 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
940 // Message type DISCOVER.
941 (byte) 0x35, (byte) 0x01, (byte) 0x01,
942 // Client identifier Ethernet, da:01:19:5b:b1:7a.
943 (byte) 0x3d, (byte) 0x07,
944 (byte) 0x01,
945 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
946 // Max message size 1500.
947 (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
948 // Version "android-dhcp-???".
949 (byte) 0x3c, (byte) 0x10,
950 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
951 // Hostname "android-01234567890abcde"
952 (byte) 0x0c, (byte) 0x18,
953 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
954 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
955 // Requested parameter list.
Erik Kline1d511ab2016-03-24 17:30:13 +0900956 (byte) 0x37, (byte) 0x0a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900957 DHCP_SUBNET_MASK,
958 DHCP_ROUTER,
959 DHCP_DNS_SERVER,
960 DHCP_DOMAIN_NAME,
961 DHCP_MTU,
Erik Kline1d511ab2016-03-24 17:30:13 +0900962 DHCP_BROADCAST_ADDRESS,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900963 DHCP_LEASE_TIME,
Erik Kline1d511ab2016-03-24 17:30:13 +0900964 DHCP_RENEWAL_TIME,
965 DHCP_REBINDING_TIME,
966 DHCP_VENDOR_INFO,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900967 // End options.
968 (byte) 0xff,
969 // Our packets are always of even length. TODO: find out why and possibly fix it.
970 (byte) 0x00
971 };
972 byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
973 assertTrue((expected.length & 1) == 0);
974 System.arraycopy(headers, 0, expected, 0, headers.length);
975 System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
976
977 byte[] actual = new byte[packet.limit()];
978 packet.get(actual);
979 String msg =
980 "Expected:\n " + Arrays.toString(expected) +
981 "\nActual:\n " + Arrays.toString(actual);
982 assertTrue(msg, Arrays.equals(expected, actual));
983 }
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900984
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +0900985 public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
986 throws Exception {
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900987 final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
988 final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
989 final int transactionId = 0xdeadbeef;
990
991 final ByteBuffer packet = DhcpPacket.buildOfferPacket(
992 DhcpPacket.ENCAP_BOOTP, transactionId, false /* broadcast */,
993 SERVER_ADDR, INADDR_ANY /* relayIp */, CLIENT_ADDR /* yourIp */,
994 CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
995 BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
996 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +0900997 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
998 false /* metered */, MTU);
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900999
1000 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1001 // BOOTP headers
1002 bos.write(new byte[] {
1003 (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x00,
1004 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
1005 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
1006 // ciaddr
1007 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
1008 });
1009 // yiaddr
1010 bos.write(CLIENT_ADDR.getAddress());
1011 // siaddr
1012 bos.write(SERVER_ADDR.getAddress());
1013 // giaddr
1014 bos.write(INADDR_ANY.getAddress());
1015 // chaddr
1016 bos.write(CLIENT_MAC);
1017
1018 // Padding
1019 bos.write(new byte[202]);
1020
1021 // Options
1022 bos.write(new byte[]{
1023 // Magic cookie 0x63825363.
1024 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
1025 // Message type OFFER.
1026 (byte) 0x35, (byte) 0x01, (byte) 0x02,
1027 });
1028 // Server ID
1029 bos.write(new byte[] { (byte) 0x36, (byte) 0x04 });
1030 bos.write(SERVER_ADDR.getAddress());
1031 // Lease time
1032 bos.write(new byte[] { (byte) 0x33, (byte) 0x04 });
1033 bos.write(intToByteArray(leaseTimeSecs));
1034 if (leaseTimeSecs != INFINITE_LEASE) {
1035 // Renewal time
1036 bos.write(new byte[]{(byte) 0x3a, (byte) 0x04});
1037 bos.write(intToByteArray(renewalTime));
1038 // Rebinding time
1039 bos.write(new byte[]{(byte) 0x3b, (byte) 0x04});
1040 bos.write(intToByteArray(rebindingTime));
1041 }
1042 // Subnet mask
1043 bos.write(new byte[] { (byte) 0x01, (byte) 0x04 });
1044 bos.write(NETMASK.getAddress());
1045 // Broadcast address
1046 bos.write(new byte[] { (byte) 0x1c, (byte) 0x04 });
1047 bos.write(BROADCAST_ADDR.getAddress());
1048 // Router
1049 bos.write(new byte[] { (byte) 0x03, (byte) 0x04 });
1050 bos.write(SERVER_ADDR.getAddress());
1051 // Nameserver
1052 bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
1053 bos.write(SERVER_ADDR.getAddress());
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001054 // Hostname
1055 if (hostname != null) {
1056 bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
1057 bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
1058 }
1059 // MTU
1060 bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
1061 bos.write(shortToByteArray(MTU));
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001062 // End options.
1063 bos.write(0xff);
1064
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001065 if ((bos.size() & 1) != 0) {
1066 bos.write(0x00);
1067 }
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001068
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001069 final byte[] expected = bos.toByteArray();
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001070 final byte[] actual = new byte[packet.limit()];
1071 packet.get(actual);
1072 final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
1073 "\nActual:\n " + HexDump.dumpHexString(actual);
1074 assertTrue(msg, Arrays.equals(expected, actual));
1075 }
1076
1077 @Test
1078 public void testOfferPacket() throws Exception {
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001079 checkBuildOfferPacket(3600, HOSTNAME);
1080 checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
1081 checkBuildOfferPacket(0x80000000, HOSTNAME);
1082 checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
1083 checkBuildOfferPacket(3600, null);
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001084 }
1085
1086 private static byte[] intToByteArray(int val) {
1087 return ByteBuffer.allocate(4).putInt(val).array();
1088 }
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001089
1090 private static byte[] shortToByteArray(short val) {
1091 return ByteBuffer.allocate(2).putShort(val).array();
1092 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +09001093}