summaryrefslogtreecommitdiff
path: root/src/android/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/net')
-rw-r--r--src/android/net/dhcp/DhcpClient.java32
-rw-r--r--src/android/net/dhcp/DhcpPacket.java64
-rw-r--r--src/android/net/dhcp/DhcpServer.java7
-rw-r--r--src/android/net/ip/IpClient.java11
-rw-r--r--src/android/net/util/ConnectivityPacketSummary.java14
5 files changed, 108 insertions, 20 deletions
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.StateMachine;
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 @@ public class DhcpClient extends StateMachine {
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 @@ public class DhcpClient extends StateMachine {
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 @@ public class DhcpClient extends StateMachine {
@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 @@ public class DhcpClient extends StateMachine {
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 @@ public class DhcpClient extends StateMachine {
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 @@ public class DhcpClient extends StateMachine {
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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
addTlv(buf, DHCP_MTU, mMtu);
}
+ addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl);
}
/**
@@ -871,6 +876,23 @@ public abstract class DhcpPacket {
}
}
+ 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 @@ public abstract class DhcpPacket {
* 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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
/**
* 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 @@ public abstract class DhcpPacket {
}
/**
+ * 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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
pkt.mSubnetMask = netMask;
pkt.mBroadcastAddress = bcAddr;
pkt.mMtu = mtu;
+ pkt.mCaptivePortalUrl = captivePortalUrl;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
@@ -1396,7 +1435,7 @@ public abstract class DhcpPacket {
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 @@ public abstract class DhcpPacket {
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 @@ public class DhcpServer extends IDhcpServer.Stub {
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 @@ public class DhcpServer extends IDhcpServer.Stub {
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.ProvisioningConfigurationParcelable;
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.text.TextUtils;
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 @@ public class IpClient extends StateMachine {
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 @@ public class ConnectivityPacketSummary {
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);