Add captive portal info to DhcpClient output
Requesting the captive portal option is flagged off by default.
The URL it provides will be used to support the captive portal API; see
RFC7710bis.
Bug: 139269711
Test: atest NetworkStackTests NetworkStackNextTests
Test: atest NetworkStackIntegrationTests NetworkStackNextIntegrationTests
Change-Id: I783466e0e60f364e79cd76af3fe43a7862d35cf2
diff --git a/apishim/30/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java b/apishim/30/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java
index 135aa63..10df01b 100644
--- a/apishim/30/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java
+++ b/apishim/30/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java
@@ -23,7 +23,6 @@
import android.os.RemoteException;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import org.json.JSONException;
import org.json.JSONObject;
@@ -70,7 +69,6 @@
.build());
}
- @VisibleForTesting
public static boolean isSupported() {
return ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q);
}
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index fbcd0f6..41e54a4 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -17,6 +17,7 @@
package android.net.dhcp;
import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
+import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL;
import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
@@ -95,6 +96,7 @@
import com.android.internal.util.TrafficStatsConstants;
import com.android.internal.util.WakeupMessage;
import com.android.networkstack.R;
+import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
import com.android.networkstack.apishim.SocketUtilsShimImpl;
import com.android.networkstack.arp.ArpPacket;
@@ -260,8 +262,9 @@
private static final SparseArray<String> sMessageNames =
MessageUtils.findMessageNames(sMessageClasses);
- // DHCP parameters that we request.
- /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
+ // DHCP parameters that we request by default.
+ @VisibleForTesting
+ /* package */ static final byte[] DEFAULT_REQUESTED_PARAMS = new byte[] {
DHCP_SUBNET_MASK,
DHCP_ROUTER,
DHCP_DNS_SERVER,
@@ -274,6 +277,21 @@
DHCP_VENDOR_INFO,
};
+ @NonNull
+ private byte[] getRequestedParams() {
+ if (isCapportApiEnabled()) {
+ final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS,
+ DEFAULT_REQUESTED_PARAMS.length + 1);
+ params[params.length - 1] = DHCP_CAPTIVE_PORTAL;
+ return params;
+ }
+ return DEFAULT_REQUESTED_PARAMS;
+ }
+
+ private static boolean isCapportApiEnabled() {
+ return CaptivePortalDataShimImpl.isSupported();
+ }
+
// DHCP flag that means "yes, we support unicast."
private static final boolean DO_UNICAST = false;
@@ -556,8 +574,10 @@
@Override
protected void handlePacket(byte[] recvbuf, int length) {
try {
+ final byte[] optionsToSkip =
+ isCapportApiEnabled() ? new byte[0] : new byte[] { DHCP_CAPTIVE_PORTAL };
final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length,
- DhcpPacket.ENCAP_L2);
+ DhcpPacket.ENCAP_L2, optionsToSkip);
if (DBG) Log.d(TAG, "Received packet: " + packet);
sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (DhcpPacket.ParseException e) {
@@ -649,7 +669,7 @@
private boolean sendDiscoverPacket() {
final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname);
+ DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname);
return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
}
@@ -663,7 +683,7 @@
final ByteBuffer packet = DhcpPacket.buildRequestPacket(
encap, mTransactionId, getSecs(), clientAddress,
DO_UNICAST, mHwAddr, requestedAddress,
- serverAddress, REQUESTED_PARAMS, mHostname);
+ serverAddress, getRequestedParams(), mHostname);
String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
" request=" + requestedAddress.getHostAddress() +
@@ -1267,7 +1287,7 @@
final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable();
final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname);
+ DO_UNICAST, getRequestedParams(), true /* rapid commit */, mHostname);
l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST);
l2Packet.payload = Arrays.copyOf(packet.array(), packet.limit());
diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java
index 9eca0b5..19af74c 100644
--- a/src/android/net/dhcp/DhcpPacket.java
+++ b/src/android/net/dhcp/DhcpPacket.java
@@ -307,6 +307,10 @@
protected static final byte DHCP_RAPID_COMMIT = 80;
protected boolean mRapidCommit;
+ @VisibleForTesting
+ public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114;
+ protected String mCaptivePortalUrl;
+
/**
* DHCP zero-length option code: pad
*/
@@ -785,6 +789,7 @@
if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
addTlv(buf, DHCP_MTU, mMtu);
}
+ addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl);
}
/**
@@ -871,6 +876,23 @@
}
}
+ private static int skipOption(ByteBuffer packet, int optionLen)
+ throws BufferUnderflowException {
+ int expectedLen = 0;
+ for (int i = 0; i < optionLen; i++) {
+ expectedLen++;
+ packet.get();
+ }
+ return expectedLen;
+ }
+
+ private static boolean shouldSkipOption(byte optionType, byte[] optionsToSkip) {
+ for (byte option : optionsToSkip) {
+ if (option == optionType) return true;
+ }
+ return false;
+ }
+
/**
* Creates a concrete DhcpPacket from the supplied ByteBuffer. The
* buffer may have an L2 encapsulation (which is the full EthernetII
@@ -881,8 +903,8 @@
* in object fields.
*/
@VisibleForTesting
- static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
- {
+ static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType, byte[] optionsToSkip)
+ throws ParseException {
// bootp parameters
int transactionId;
short secs;
@@ -900,6 +922,7 @@
String vendorId = null;
String vendorInfo = null;
boolean rapidCommit = false;
+ String captivePortalUrl = null;
byte[] expectedParams = null;
String hostName = null;
String domainName = null;
@@ -1080,6 +1103,11 @@
int optionLen = packet.get() & 0xFF;
int expectedLen = 0;
+ if (shouldSkipOption(optionType, optionsToSkip)) {
+ skipOption(packet, optionLen);
+ continue;
+ }
+
switch(optionType) {
case DHCP_SUBNET_MASK:
netMask = readIpAddress(packet);
@@ -1172,12 +1200,12 @@
expectedLen = 0;
rapidCommit = true;
break;
+ case DHCP_CAPTIVE_PORTAL:
+ expectedLen = optionLen;
+ captivePortalUrl = readAsciiString(packet, optionLen, true);
+ break;
default:
- // ignore any other parameters
- for (int i = 0; i < optionLen; i++) {
- expectedLen++;
- byte throwaway = packet.get();
- }
+ expectedLen = skipOption(packet, optionLen);
}
if (expectedLen != optionLen) {
@@ -1263,6 +1291,7 @@
newPacket.mT2 = T2;
newPacket.mVendorId = vendorId;
newPacket.mVendorInfo = vendorInfo;
+ newPacket.mCaptivePortalUrl = captivePortalUrl;
if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) {
newPacket.mServerHostName = serverHostName;
} else {
@@ -1274,11 +1303,11 @@
/**
* Parse a packet from an array of bytes, stopping at the given length.
*/
- public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
- throws ParseException {
+ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType,
+ byte[] optionsToSkip) throws ParseException {
ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
try {
- return decodeFullPacket(buffer, pktType);
+ return decodeFullPacket(buffer, pktType, optionsToSkip);
} catch (ParseException e) {
throw e;
} catch (Exception e) {
@@ -1287,6 +1316,14 @@
}
/**
+ * Parse a packet from an array of bytes, stopping at the given length.
+ */
+ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
+ throws ParseException {
+ return decodeFullPacket(packet, length, pktType, new byte[0]);
+ }
+
+ /**
* Construct a DhcpResults object from a DHCP reply packet.
*/
public DhcpResults toDhcpResults() {
@@ -1328,6 +1365,7 @@
results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
results.serverHostName = mServerHostName;
+ results.captivePortalApiUrl = mCaptivePortalUrl;
return results;
}
@@ -1369,7 +1407,7 @@
Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu) {
+ short mtu, String captivePortalUrl) {
DhcpPacket pkt = new DhcpOfferPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1382,6 +1420,7 @@
pkt.mSubnetMask = netMask;
pkt.mBroadcastAddress = bcAddr;
pkt.mMtu = mtu;
+ pkt.mCaptivePortalUrl = captivePortalUrl;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
@@ -1396,7 +1435,7 @@
Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu, boolean rapidCommit) {
+ short mtu, boolean rapidCommit, String captivePortalUrl) {
DhcpPacket pkt = new DhcpAckPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
mac, rapidCommit);
@@ -1409,6 +1448,7 @@
pkt.mServerIdentifier = dhcpServerIdentifier;
pkt.mBroadcastAddress = bcAddr;
pkt.mMtu = mtu;
+ pkt.mCaptivePortalUrl = captivePortalUrl;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java
index 9e54a69..9c5b3c6 100644
--- a/src/android/net/dhcp/DhcpServer.java
+++ b/src/android/net/dhcp/DhcpServer.java
@@ -529,7 +529,9 @@
broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
new ArrayList<>(mServingParams.dnsServers),
mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
- mServingParams.metered, (short) mServingParams.linkMtu);
+ mServingParams.metered, (short) mServingParams.linkMtu,
+ // TODO (b/144402437): advertise the URL if known
+ null /* captivePortalApiUrl */);
return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
}
@@ -549,7 +551,8 @@
new ArrayList<>(mServingParams.dnsServers),
mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
mServingParams.metered, (short) mServingParams.linkMtu,
- packet.mRapidCommit && mDhcpRapidCommitEnabled);
+ // TODO (b/144402437): advertise the URL if known
+ packet.mRapidCommit && mDhcpRapidCommitEnabled, null /* captivePortalApiUrl */);
return transmitOfferOrAckPacket(ackPacket, packet, lease, clientMac, broadcastFlag);
}
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 47f955a..173f136 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -36,6 +36,7 @@
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.TcpKeepalivePacketDataParcelable;
+import android.net.Uri;
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.dhcp.DhcpClient;
@@ -57,6 +58,7 @@
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
+import android.util.Patterns;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -1192,6 +1194,15 @@
if (mDhcpResults.mtu != 0) {
newLp.setMtu(mDhcpResults.mtu);
}
+
+ final String capportUrl = mDhcpResults.captivePortalApiUrl;
+ // Uri.parse does no syntax check; do a simple regex check to eliminate garbage.
+ // If the URL is still incorrect data fetching will fail later, which is fine.
+ if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) {
+ NetworkInformationShimImpl.newInstance()
+ .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl));
+ }
+ // TODO: also look at the IPv6 RA (netlink) for captive portal URL
}
// [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
diff --git a/src/android/net/util/ConnectivityPacketSummary.java b/src/android/net/util/ConnectivityPacketSummary.java
index 08c3f60..4d04911 100644
--- a/src/android/net/util/ConnectivityPacketSummary.java
+++ b/src/android/net/util/ConnectivityPacketSummary.java
@@ -82,12 +82,26 @@
private final ByteBuffer mPacket;
private final String mSummary;
+ /**
+ * Create a string summary of a received packet.
+ * @param hwaddr MacAddress of the receiving device.
+ * @param buffer Buffer of the packet. Length is assumed to be the buffer length.
+ * @return A summary of the packet.
+ */
public static String summarize(MacAddress hwaddr, byte[] buffer) {
return summarize(hwaddr, buffer, buffer.length);
}
// Methods called herein perform some but by no means all error checking.
// They may throw runtime exceptions on malformed packets.
+
+ /**
+ * Create a string summary of a received packet.
+ * @param macAddr MacAddress of the receiving device.
+ * @param buffer Buffer of the packet.
+ * @param length Length of the packet.
+ * @return A summary of the packet.
+ */
public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
if ((macAddr == null) || (buffer == null)) return null;
length = Math.min(length, buffer.length);
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index c4f057b..a9eabc8 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -27,9 +27,8 @@
visibility: ["//visibility:private"],
}
-android_library {
- name: "NetworkStackIntegrationTestsLib",
- min_sdk_version: "29",
+java_defaults {
+ name: "NetworkStackIntegrationTestsDefaults",
srcs: ["src/**/*.java"],
static_libs: [
"androidx.annotation_annotation",
@@ -37,7 +36,6 @@
"mockito-target-extended-minus-junit4",
"net-tests-utils",
"testables",
- "NetworkStackApiStableLib",
],
libs: [
"android.test.runner",
@@ -48,6 +46,15 @@
visibility: ["//visibility:private"],
}
+android_library {
+ name: "NetworkStackIntegrationTestsLib",
+ defaults: ["NetworkStackIntegrationTestsDefaults"],
+ min_sdk_version: "29",
+ static_libs: [
+ "NetworkStackApiStableLib",
+ ],
+}
+
// Network stack integration tests.
android_test {
name: "NetworkStackIntegrationTests",
@@ -59,6 +66,21 @@
min_sdk_version: "29",
}
+// Network stack next integration tests.
+android_test {
+ name: "NetworkStackNextIntegrationTests",
+ defaults: [
+ "NetworkStackIntegrationTestsDefaults",
+ "NetworkStackIntegrationTestsJniDefaults",
+ ],
+ static_libs: [
+ "NetworkStackApiCurrentLib",
+ ],
+ certificate: "networkstack",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
+
// Special version of the network stack tests that includes all tests necessary for code coverage
// purposes. This is currently the union of NetworkStackTests and NetworkStackIntegrationTests.
android_test {
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
index 4bbee9a..b3ca925 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
@@ -56,6 +56,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
@@ -86,6 +88,7 @@
import android.net.NetworkStackIpMemoryStore;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
+import android.net.Uri;
import android.net.dhcp.DhcpClient;
import android.net.dhcp.DhcpDeclinePacket;
import android.net.dhcp.DhcpDiscoverPacket;
@@ -117,6 +120,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
import com.android.networkstack.apishim.ShimUtils;
import com.android.networkstack.arp.ArpPacket;
import com.android.server.NetworkObserverRegistry;
@@ -146,6 +150,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -219,6 +224,7 @@
private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 };
private static final String TEST_HOST_NAME = "AOSP on Crosshatch";
private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch";
+ private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi";
private static class TapPacketReader extends PacketReader {
private final ParcelFileDescriptor mTapFd;
@@ -484,7 +490,7 @@
}
private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
- final Integer leaseTimeSec, final short mtu) {
+ final Integer leaseTimeSec, final short mtu, final String captivePortalUrl) {
return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
CLIENT_ADDR /* yourIp */, packet.getClientMac(), leaseTimeSec,
@@ -492,11 +498,12 @@
Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
- false /* metered */, mtu);
+ false /* metered */, mtu, captivePortalUrl);
}
private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
- final Integer leaseTimeSec, final short mtu, final boolean rapidCommit) {
+ final Integer leaseTimeSec, final short mtu, final boolean rapidCommit,
+ final String captivePortalApiUrl) {
return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
CLIENT_ADDR /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(),
@@ -504,7 +511,7 @@
Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
- false /* metered */, mtu, rapidCommit);
+ false /* metered */, mtu, rapidCommit, captivePortalApiUrl);
}
private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) {
@@ -624,27 +631,35 @@
final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
final boolean shouldReplyRapidCommitAck, final int mtu,
final boolean isDhcpIpConflictDetectEnabled,
- final boolean isHostnameConfigurationEnabled, final String hostname)
- throws Exception {
- final List<DhcpPacket> packetList = new ArrayList<DhcpPacket>();
+ final boolean isHostnameConfigurationEnabled, final String hostname,
+ final String captivePortalApiUrl) throws Exception {
startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
isHostnameConfigurationEnabled, hostname);
+ return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
+ isDhcpIpConflictDetectEnabled, captivePortalApiUrl);
+ }
+ private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease,
+ final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
+ final boolean isDhcpIpConflictDetectEnabled, final String captivePortalApiUrl)
+ throws Exception {
+ final List<DhcpPacket> packetList = new ArrayList<>();
DhcpPacket packet;
while ((packet = getNextDhcpPacket()) != null) {
packetList.add(packet);
if (packet instanceof DhcpDiscoverPacket) {
if (shouldReplyRapidCommitAck) {
sendResponse(buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
- true /* rapidCommit */));
+ true /* rapidCommit */, captivePortalApiUrl));
} else {
- sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec, (short) mtu));
+ sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec, (short) mtu,
+ captivePortalApiUrl));
}
} else if (packet instanceof DhcpRequestPacket) {
final ByteBuffer byteBuffer = isSuccessLease
? buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
- false /* rapidCommit */)
+ false /* rapidCommit */, captivePortalApiUrl)
: buildDhcpNakPacket(packet);
sendResponse(byteBuffer);
} else {
@@ -676,7 +691,8 @@
final boolean isDhcpIpConflictDetectEnabled) throws Exception {
return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled,
isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled,
- false /* isHostnameConfigurationEnabled */, null /* hostname */);
+ false /* isHostnameConfigurationEnabled */, null /* hostname */,
+ null /* captivePortalApiUrl */);
}
private DhcpPacket getNextDhcpPacket() throws ParseException {
@@ -812,12 +828,13 @@
final short mtu = (short) TEST_DEFAULT_MTU;
if (!shouldReplyRapidCommitAck) {
- sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu));
+ sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu,
+ null /* captivePortalUrl */));
packet = getNextDhcpPacket();
assertTrue(packet instanceof DhcpRequestPacket);
}
sendResponse(buildDhcpAckPacket(packet, TEST_LEASE_DURATION_S, mtu,
- shouldReplyRapidCommitAck));
+ shouldReplyRapidCommitAck, null /* captivePortalUrl */));
if (!shouldAbortPreconnection) {
mIpc.notifyPreconnectionComplete(true /* success */);
@@ -1447,7 +1464,8 @@
TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
- true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */);
+ true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */,
+ null /* captivePortalApiUrl */);
assertEquals(2, sentPackets.size());
assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -1460,7 +1478,8 @@
TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
- false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME);
+ false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME,
+ null /* captivePortalApiUrl */);
assertEquals(2, sentPackets.size());
assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -1473,10 +1492,59 @@
TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
- true /* isHostnameConfigurationEnabled */, null /* hostname */);
+ true /* isHostnameConfigurationEnabled */, null /* hostname */,
+ null /* captivePortalApiUrl */);
assertEquals(2, sentPackets.size());
assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */,
sentPackets);
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
+
+ private void runDhcpClientCaptivePortalApiTest(boolean featureEnabled,
+ boolean serverSendsOption) throws Exception {
+ startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
+ false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
+ false /* isDhcpIpConflictDetectEnabled */);
+ final DhcpPacket discover = getNextDhcpPacket();
+ assertTrue(discover instanceof DhcpDiscoverPacket);
+ assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL));
+
+ // Send Offer and handle Request -> Ack
+ final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
+ sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU,
+ serverSentUrl));
+ final int testMtu = 1345;
+ handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
+ false /* isDhcpRapidCommitEnabled */, testMtu,
+ false /* isDhcpIpConflictDetectEnabled */, serverSentUrl);
+
+ final Uri expectedUrl = featureEnabled && serverSendsOption
+ ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null;
+ // Wait for LinkProperties containing DHCP-obtained info, such as MTU, and ensure that the
+ // URL is set as expected
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(lp ->
+ lp.getMtu() == testMtu
+ && Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl())));
+ }
+
+ @Test
+ public void testDhcpClientCaptivePortalApiEnabled() throws Exception {
+ // Only run the test on platforms / builds where the API is enabled
+ assumeTrue(CaptivePortalDataShimImpl.isSupported());
+ runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */);
+ }
+
+ @Test
+ public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception {
+ // Only run the test on platforms / builds where the API is enabled
+ assumeTrue(CaptivePortalDataShimImpl.isSupported());
+ runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */);
+ }
+
+ @Test
+ public void testDhcpClientCaptivePortalApiDisabled() throws Exception {
+ // Only run the test on platforms / builds where the API is disabled
+ assumeFalse(CaptivePortalDataShimImpl.isSupported());
+ runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */);
+ }
}
diff --git a/tests/unit/src/android/net/dhcp/DhcpPacketTest.java b/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
index 090631b..6ce1fdf 100644
--- a/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
@@ -78,10 +78,12 @@
private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
SERVER_ADDR, PREFIX_LENGTH);
private static final String HOSTNAME = "testhostname";
+ private static final String CAPTIVE_PORTAL_API_URL = "https://example.com/capportapi";
private static final short MTU = 1500;
// Use our own empty address instead of IPV4_ADDR_ANY or INADDR_ANY to ensure that the code
// doesn't use == instead of equals when comparing addresses.
private static final Inet4Address ANY = v4Address("0.0.0.0");
+ private static final byte[] TEST_EMPTY_OPTIONS_SKIP_LIST = new byte[0];
private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
@@ -169,7 +171,8 @@
.setDomainBytes(domainBytes)
.setVendorInfoBytes(vendorInfoBytes)
.build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertEquals(expectedDomain, offerPacket.mDomainName);
assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
}
@@ -215,14 +218,16 @@
if (!expectValid) {
try {
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
fail("Invalid packet parsed successfully: " + offerPacket);
} catch (ParseException expected) {
}
return;
}
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertNotNull(offerPacket);
assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
@@ -266,7 +271,8 @@
ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
.setNetmaskBytes(netmaskBytes)
.build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
DhcpResults results = offerPacket.toDhcpResults();
if (expected != null) {
@@ -351,7 +357,8 @@
"3a0400000e103b040000189cff00000000000000000000"));
// CHECKSTYLE:ON Generated code
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
@@ -384,7 +391,8 @@
// CHECKSTYLE:ON Generated code
assertEquals(337, packet.limit());
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
@@ -393,6 +401,88 @@
assertTrue(dhcpResults.hasMeteredHint());
}
+ private void runCapportOptionTest(boolean enabled) throws Exception {
+ // CHECKSTYLE:OFF Generated code
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header
+ "00430044013d9ac7" +
+ // BOOTP header
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator).
+ "646863702e616e64726f69642e636f6d00000000000000000000000000000000" +
+ "0000000000004141414100000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options
+ "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
+ "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544721d" +
+ "68747470733a2f2f706f7274616c6170692e6578616d706c652e636f6dff"));
+ // CHECKSTYLE:ON Generated code
+
+ final DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ enabled ? TEST_EMPTY_OPTIONS_SKIP_LIST
+ : new byte[] { DhcpPacket.DHCP_CAPTIVE_PORTAL });
+ assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
+ final DhcpResults dhcpResults = offerPacket.toDhcpResults();
+ final String testUrl = enabled ? "https://portalapi.example.com" : null;
+ assertEquals(testUrl, dhcpResults.captivePortalApiUrl);
+ }
+
+ @Test
+ public void testCapportOption() throws Exception {
+ runCapportOptionTest(true /* enabled */);
+ }
+
+ @Test
+ public void testCapportOption_Disabled() throws Exception {
+ runCapportOptionTest(false /* enabled */);
+ }
+
+ @Test
+ public void testCapportOption_Invalid() throws Exception {
+ // CHECKSTYLE:OFF Generated code
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header
+ "00430044013d9ac7" +
+ // BOOTP header
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator).
+ "646863702e616e64726f69642e636f6d00000000000000000000000000000000" +
+ "0000000000004141414100000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options
+ "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
+ "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544" +
+ // Option 114 (0x72, capport), length 10 (0x0a)
+ "720a" +
+ // バグ-com in UTF-8, plus the ff byte that marks the end of options.
+ "e38390e382b02d636f6dff"));
+ // CHECKSTYLE:ON Generated code
+
+ final DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
+ assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
+ final DhcpResults dhcpResults = offerPacket.toDhcpResults();
+ // Output URL will be garbled because some characters do not exist in the target charset,
+ // but the parser should not crash.
+ assertTrue(dhcpResults.captivePortalApiUrl.length() > 0);
+ }
+
@Test
public void testBadIpPacket() throws Exception {
final byte[] packet = HexDump.hexStringToByteArray(
@@ -400,7 +490,8 @@
"450001518d0600004011144dc0a82b01c0a82bf7");
try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
} catch (DhcpPacket.ParseException expected) {
assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
return;
@@ -419,7 +510,7 @@
"02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
} catch (DhcpPacket.ParseException expected) {
assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
return;
@@ -448,7 +539,7 @@
"00000000000000000000000000000000000000000000000000000000000000");
try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
} catch (DhcpPacket.ParseException expected) {
assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
return;
@@ -479,7 +570,7 @@
);
try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
} catch (DhcpPacket.ParseException expected) {
assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode);
return;
@@ -511,7 +602,7 @@
"1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
} catch (DhcpPacket.ParseException expected) {
assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode);
return;
@@ -590,7 +681,8 @@
packet.put(mtuBytes);
packet.clear();
}
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
@@ -665,7 +757,8 @@
assertEquals(6, packet.get(hwAddrLenOffset));
// Expect the expected.
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertNotNull(offerPacket);
assertEquals(6, offerPacket.getClientMac().length);
assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
@@ -673,7 +766,7 @@
// Reduce the hardware address length and verify that it shortens the client MAC.
packet.flip();
packet.put(hwAddrLenOffset, (byte) 5);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3, TEST_EMPTY_OPTIONS_SKIP_LIST);
assertNotNull(offerPacket);
assertEquals(5, offerPacket.getClientMac().length);
assertEquals(expectedClientMac.substring(0, 10),
@@ -681,7 +774,7 @@
packet.flip();
packet.put(hwAddrLenOffset, (byte) 3);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3, TEST_EMPTY_OPTIONS_SKIP_LIST);
assertNotNull(offerPacket);
assertEquals(3, offerPacket.getClientMac().length);
assertEquals(expectedClientMac.substring(0, 6),
@@ -691,7 +784,7 @@
// and crash, and b) hardcode it to 6.
packet.flip();
packet.put(hwAddrLenOffset, (byte) -1);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3, TEST_EMPTY_OPTIONS_SKIP_LIST);
assertNotNull(offerPacket);
assertEquals(6, offerPacket.getClientMac().length);
assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
@@ -700,7 +793,7 @@
// hardcode it to 6.
packet.flip();
packet.put(hwAddrLenOffset, (byte) 17);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3, TEST_EMPTY_OPTIONS_SKIP_LIST);
assertNotNull(offerPacket);
assertEquals(6, offerPacket.getClientMac().length);
assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
@@ -740,7 +833,8 @@
"0000000000000000000000000000000000000000000000ff000000"));
// CHECKSTYLE:ON Generated code
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket);
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
@@ -772,7 +866,8 @@
"0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
// CHECKSTYLE:ON Generated code
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket);
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
@@ -806,7 +901,8 @@
"0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
// CHECKSTYLE:ON Generated code
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket);
assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
DhcpResults dhcpResults = offerPacket.toDhcpResults();
@@ -842,7 +938,8 @@
"d18180060f0777766d2e6564751c040a0fffffff000000"));
// CHECKSTYLE:ON Generated code
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket);
assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
DhcpResults dhcpResults = offerPacket.toDhcpResults();
@@ -880,7 +977,7 @@
// CHECKSTYLE:ON Generated code
try {
- DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ DhcpPacket.decodeFullPacket(packet, ENCAP_L2, TEST_EMPTY_OPTIONS_SKIP_LIST);
fail("Packet with invalid dst port did not throw ParseException");
} catch (ParseException expected) {}
}
@@ -912,7 +1009,8 @@
"0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
// CHECKSTYLE:ON Generated code
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2,
+ TEST_EMPTY_OPTIONS_SKIP_LIST);
assertTrue(offerPacket instanceof DhcpOfferPacket);
assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
DhcpResults dhcpResults = offerPacket.toDhcpResults();
@@ -931,8 +1029,8 @@
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
- false /* do unicast */, DhcpClient.REQUESTED_PARAMS, false /* rapid commit */,
- testHostname);
+ false /* do unicast */, DhcpClient.DEFAULT_REQUESTED_PARAMS,
+ false /* rapid commit */, testHostname);
final byte[] headers = new byte[] {
// Ethernet header.
@@ -1021,7 +1119,7 @@
BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
- false /* metered */, MTU);
+ false /* metered */, MTU, CAPTIVE_PORTAL_API_URL);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// BOOTP headers
@@ -1085,6 +1183,9 @@
// MTU
bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
bos.write(shortToByteArray(MTU));
+ // capport URL. Option 114 = 0x72
+ bos.write(new byte[] { (byte) 0x72, (byte) CAPTIVE_PORTAL_API_URL.length() });
+ bos.write(CAPTIVE_PORTAL_API_URL.getBytes(Charset.forName("US-ASCII")));
// End options.
bos.write(0xff);
diff --git a/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
index a1613c5..a278a88 100644
--- a/tests/unit/src/android/net/dhcp/DhcpServerTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
@@ -383,7 +383,8 @@
private DhcpPacket getPacket() throws Exception {
verify(mDeps, times(1)).sendPacket(any(), any(), any());
- return DhcpPacket.decodeFullPacket(mSentPacketCaptor.getValue(), ENCAP_BOOTP);
+ return DhcpPacket.decodeFullPacket(mSentPacketCaptor.getValue(), ENCAP_BOOTP,
+ new byte[0] /* optionsToSkip */);
}
private static Inet4Address parseAddr(@Nullable String inet4Addr) {