Loading res/values/config.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -46,4 +46,7 @@ <!-- Set to true if NetworkMonitor needs to load the resource by neighbor mcc when device <!-- Set to true if NetworkMonitor needs to load the resource by neighbor mcc when device doesn't have a SIM card inserted. --> doesn't have a SIM card inserted. --> <bool name="config_no_sim_card_uses_neighbor_mcc">false</bool> <bool name="config_no_sim_card_uses_neighbor_mcc">false</bool> <!-- Configuration for including DHCP client hostname option --> <bool name="config_dhcp_client_hostname">false</bool> </resources> </resources> res/values/overlayable.xml +10 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,16 @@ <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <!-- Configuration value for DhcpResults --> <!-- Configuration value for DhcpResults --> <item type="array" name="config_default_dns_servers"/> <item type="array" name="config_default_dns_servers"/> <!-- Configuration for including DHCP client hostname option. If this option is true, client hostname set in Settings.Global.DEVICE_NAME will be included in DHCPDISCOVER/DHCPREQUEST, otherwise, the DHCP hostname option will not be sent. RFC952 and RFC1123 stipulates an valid hostname should be only comprised of 'a-z', 'A-Z' and '-', and the length should be up to 63 octets or less (RFC1035#2.3.4), platform will perform best-effort transliteration for other characters. Anything that could be used to identify the device uniquely is not recommended, e.g. user's name, random number and etc. --> <item type="bool" name="config_dhcp_client_hostname"/> </policy> </policy> </overlayable> </overlayable> </resources> </resources> src/android/net/dhcp/DhcpClient.java +88 −60 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.net.ipmemorystore.OnStatusListener; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpConnectivityLog; import android.net.util.HostnameTransliterator; import android.net.util.InterfaceParams; import android.net.util.InterfaceParams; import android.net.util.NetworkStackUtils; import android.net.util.NetworkStackUtils; import android.net.util.PacketReader; import android.net.util.PacketReader; Loading @@ -76,6 +77,7 @@ import android.os.Handler; import android.os.Message; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.ErrnoException; import android.system.Os; import android.system.Os; import android.util.EventLog; import android.util.EventLog; Loading Loading @@ -154,34 +156,40 @@ public class DhcpClient extends StateMachine { // seconds to avoid excessive traffic, but it's too long). // seconds to avoid excessive traffic, but it's too long). @VisibleForTesting @VisibleForTesting public static final String DHCP_RESTART_CONFIG_DELAY = "dhcp_restart_configuration_delay"; public static final String DHCP_RESTART_CONFIG_DELAY = "dhcp_restart_configuration_delay"; private static final int DEFAULT_DHCP_RESTART_CONFIG_DELAY_SEC = 1 * SECONDS; private static final int DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS = 1 * SECONDS; private static final int MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS = 10 * SECONDS; // Initial random delay before sending first ARP probe. // Initial random delay before sending first ARP probe. @VisibleForTesting @VisibleForTesting public static final String ARP_FIRST_PROBE_DELAY_MS = "arp_first_probe_delay"; public static final String ARP_FIRST_PROBE_DELAY_MS = "arp_first_probe_delay"; private static final int DEFAULT_ARP_FIRST_PROBE_DELAY_MS = 100; private static final int DEFAULT_ARP_FIRST_PROBE_DELAY_MS = 100; private static final int MAX_ARP_FIRST_PROBE_DELAY_MS = 1 * SECONDS; // Minimum delay until retransmitting the probe. The probe will be retransmitted after a // Minimum delay until retransmitting the probe. The probe will be retransmitted after a // random number of milliseconds in the range ARP_PROBE_MIN_MS and ARP_PROBE_MAX_MS. // random number of milliseconds in the range ARP_PROBE_MIN_MS and ARP_PROBE_MAX_MS. @VisibleForTesting @VisibleForTesting public static final String ARP_PROBE_MIN_MS = "arp_probe_min"; public static final String ARP_PROBE_MIN_MS = "arp_probe_min"; private static final int DEFAULT_ARP_PROBE_MIN_MS = 100; private static final int DEFAULT_ARP_PROBE_MIN_MS = 100; private static final int MAX_ARP_PROBE_MIN_MS = 1 * SECONDS; // Maximum delay until retransmitting the probe. // Maximum delay until retransmitting the probe. @VisibleForTesting @VisibleForTesting public static final String ARP_PROBE_MAX_MS = "arp_probe_max"; public static final String ARP_PROBE_MAX_MS = "arp_probe_max"; private static final int DEFAULT_ARP_PROBE_MAX_MS = 300; private static final int DEFAULT_ARP_PROBE_MAX_MS = 300; private static final int MAX_ARP_PROBE_MAX_MS = 2 * SECONDS; // Initial random delay before sending first ARP Announcement after completing Probe packet // Initial random delay before sending first ARP Announcement after completing Probe packet // transmission. // transmission. @VisibleForTesting @VisibleForTesting public static final String ARP_FIRST_ANNOUNCE_DELAY_MS = "arp_first_announce_delay"; public static final String ARP_FIRST_ANNOUNCE_DELAY_MS = "arp_first_announce_delay"; private static final int DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS = 100; private static final int DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS = 100; private static final int MAX_ARP_FIRST_ANNOUNCE_DELAY_MS = 2 * SECONDS; // Time between retransmitting ARP Announcement packets. // Time between retransmitting ARP Announcement packets. @VisibleForTesting @VisibleForTesting public static final String ARP_ANNOUNCE_INTERVAL_MS = "arp_announce_interval"; public static final String ARP_ANNOUNCE_INTERVAL_MS = "arp_announce_interval"; private static final int DEFAULT_ARP_ANNOUNCE_INTERVAL_MS = 100; private static final int DEFAULT_ARP_ANNOUNCE_INTERVAL_MS = 100; private static final int MAX_ARP_ANNOUNCE_INTERVAL_MS = 2 * SECONDS; // Max conflict count before configuring interface with declined IP address anyway. // Max conflict count before configuring interface with declined IP address anyway. private static final int MAX_CONFLICTS_COUNT = 2; private static final int MAX_CONFLICTS_COUNT = 2; Loading Loading @@ -306,6 +314,8 @@ public class DhcpClient extends StateMachine { private final NetworkStackIpMemoryStore mIpMemoryStore; private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable @Nullable private DhcpPacketHandler mDhcpPacketHandler; private DhcpPacketHandler mDhcpPacketHandler; @Nullable private final String mHostname; // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. private long mLastInitEnterTime; private long mLastInitEnterTime; Loading Loading @@ -349,6 +359,22 @@ public class DhcpClient extends StateMachine { mNetworkStackIpMemoryStore = store; mNetworkStackIpMemoryStore = store; } } /** * Get the configuration from RRO to check whether or not to send hostname option in * DHCPDISCOVER/DHCPREQUEST message. */ public boolean getSendHostnameOption(final Context context) { return context.getResources().getBoolean(R.bool.config_dhcp_client_hostname); } /** * Get the device name from system settings. */ public String getDeviceName(final Context context) { return Settings.Global.getString(context.getContentResolver(), Settings.Global.DEVICE_NAME); } /** /** * Get a IpMemoryStore instance. * Get a IpMemoryStore instance. */ */ Loading @@ -367,33 +393,10 @@ public class DhcpClient extends StateMachine { /** /** * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace. * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace. */ */ public int getIntDeviceConfig(final String name) { public int getIntDeviceConfig(final String name, int minimumValue, int maximumValue, final int defaultValue; int defaultValue) { switch (name) { return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, case DHCP_RESTART_CONFIG_DELAY: name, minimumValue, maximumValue, defaultValue); defaultValue = DEFAULT_DHCP_RESTART_CONFIG_DELAY_SEC; break; case ARP_FIRST_PROBE_DELAY_MS: defaultValue = DEFAULT_ARP_FIRST_PROBE_DELAY_MS; break; case ARP_PROBE_MIN_MS: defaultValue = DEFAULT_ARP_PROBE_MIN_MS; break; case ARP_PROBE_MAX_MS: defaultValue = DEFAULT_ARP_PROBE_MAX_MS; break; case ARP_FIRST_ANNOUNCE_DELAY_MS: defaultValue = DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS; break; case ARP_ANNOUNCE_INTERVAL_MS: defaultValue = DEFAULT_ARP_ANNOUNCE_INTERVAL_MS; break; default: Log.e(TAG, "Invalid experiment flag: " + name); return 0; } return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, name, defaultValue); } } /** /** Loading Loading @@ -450,6 +453,11 @@ public class DhcpClient extends StateMachine { mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP); mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP); mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP); mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP); // Transliterate hostname read from system settings if RRO option is enabled. final boolean sendHostname = deps.getSendHostnameOption(context); mHostname = sendHostname ? new HostnameTransliterator().transliterate( deps.getDeviceName(mContext)) : null; } } public void registerForPreDhcpNotification() { public void registerForPreDhcpNotification() { Loading Loading @@ -641,7 +649,7 @@ public class DhcpClient extends StateMachine { private boolean sendDiscoverPacket() { private boolean sendDiscoverPacket() { final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled()); DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } } Loading @@ -655,7 +663,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildRequestPacket( final ByteBuffer packet = DhcpPacket.buildRequestPacket( encap, mTransactionId, getSecs(), clientAddress, encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, DO_UNICAST, mHwAddr, requestedAddress, serverAddress, REQUESTED_PARAMS, null); serverAddress, REQUESTED_PARAMS, mHostname); String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + Loading Loading @@ -744,7 +752,7 @@ public class DhcpClient extends StateMachine { mDhcpLease = results; mDhcpLease = results; if (mDhcpLease.dnsServers.isEmpty()) { if (mDhcpLease.dnsServers.isEmpty()) { // supplement customized dns servers // supplement customized dns servers String[] dnsServersList = final String[] dnsServersList = mContext.getResources().getStringArray(R.array.config_default_dns_servers); mContext.getResources().getStringArray(R.array.config_default_dns_servers); for (final String dnsServer : dnsServersList) { for (final String dnsServer : dnsServersList) { try { try { Loading Loading @@ -1259,7 +1267,7 @@ public class DhcpClient extends StateMachine { final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */); DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.payload = packet.array(); l2Packet.payload = packet.array(); Loading Loading @@ -1429,16 +1437,16 @@ public class DhcpClient extends StateMachine { // address being probed for, and the packet's 'sender hardware address' is not the // address being probed for, and the packet's 'sender hardware address' is not the // hardware address of any of the host's interfaces, then the host SHOULD similarly // hardware address of any of the host's interfaces, then the host SHOULD similarly // treat this as an address conflict. // treat this as an address conflict. private boolean checkArpSenderIpOrTargetIp(@NonNull ArpPacket packet, private boolean packetHasIpAddressConflict(@NonNull ArpPacket packet, @NonNull Inet4Address targetIp) { @NonNull Inet4Address targetIp) { return ((!packet.senderIp.equals(INADDR_ANY) && packet.senderIp.equals(targetIp)) return (((!packet.senderIp.equals(INADDR_ANY) && packet.senderIp.equals(targetIp)) || (isArpProbe(packet) && packet.targetIp.equals(targetIp))); || (isArpProbe(packet) && packet.targetIp.equals(targetIp))) && !Arrays.equals(packet.senderHwAddress.toByteArray(), mHwAddr)); } } private boolean hasIpAddressConflict(@NonNull ArpPacket packet, private boolean hasIpAddressConflict(@NonNull ArpPacket packet, @NonNull Inet4Address targetIp) { @NonNull Inet4Address targetIp) { if (checkArpSenderIpOrTargetIp(packet, targetIp) if (!packetHasIpAddressConflict(packet, targetIp)) return false; && !Arrays.equals(packet.senderHwAddress.toByteArray(), mHwAddr)) { if (DBG) { if (DBG) { final String senderIpString = packet.senderIp.getHostAddress(); final String senderIpString = packet.senderIp.getHostAddress(); final String targetIpString = packet.targetIp.getHostAddress(); final String targetIpString = packet.targetIp.getHostAddress(); Loading @@ -1454,8 +1462,6 @@ public class DhcpClient extends StateMachine { } } return true; return true; } } return false; } class IpAddressConflictDetectingState extends LoggingState { class IpAddressConflictDetectingState extends LoggingState { private int mArpProbeCount; private int mArpProbeCount; Loading Loading @@ -1492,13 +1498,7 @@ public class DhcpClient extends StateMachine { } } // Read the customized parameters from DeviceConfig. // Read the customized parameters from DeviceConfig. mArpFirstProbeDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_PROBE_DELAY_MS); readIpConflictParametersFromDeviceConfig(); mArpProbeMaxDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MAX_MS); mArpProbeMinDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MIN_MS); mArpAnnounceIntervalMs = mDependencies.getIntDeviceConfig(ARP_ANNOUNCE_INTERVAL_MS); mArpFirstAnnounceDelayMs = mDependencies.getIntDeviceConfig( ARP_FIRST_ANNOUNCE_DELAY_MS); if (VDBG) { if (VDBG) { Log.d(TAG, "ARP First Probe delay: " + mArpFirstProbeDelayMs Log.d(TAG, "ARP First Probe delay: " + mArpFirstProbeDelayMs + " ARP Probe Max delay: " + mArpProbeMaxDelayMs + " ARP Probe Max delay: " + mArpProbeMaxDelayMs Loading Loading @@ -1577,6 +1577,33 @@ public class DhcpClient extends StateMachine { removeMessages(EVENT_IP_CONFLICT); removeMessages(EVENT_IP_CONFLICT); } } // The following timing parameters are defined in RFC5227 as fixed constants, which // are too long to adopt in the mobile network scenario, however more appropriate to // reference these fixed value as maximumValue argument to restrict the upper bound, // the minimum values of 10/20ms are used to avoid tight loops due to misconfiguration. private void readIpConflictParametersFromDeviceConfig() { // PROBE_WAIT mArpFirstProbeDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_PROBE_DELAY_MS, 10, MAX_ARP_FIRST_PROBE_DELAY_MS, DEFAULT_ARP_FIRST_PROBE_DELAY_MS); // PROBE_MIN mArpProbeMinDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MIN_MS, 10, MAX_ARP_PROBE_MIN_MS, DEFAULT_ARP_PROBE_MIN_MS); // PROBE_MAX mArpProbeMaxDelayMs = Math.max(mArpProbeMinDelayMs + 1, mDependencies.getIntDeviceConfig(ARP_PROBE_MAX_MS, 20, MAX_ARP_PROBE_MAX_MS, DEFAULT_ARP_PROBE_MAX_MS)); // ANNOUNCE_WAIT mArpFirstAnnounceDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_ANNOUNCE_DELAY_MS, 20, MAX_ARP_FIRST_ANNOUNCE_DELAY_MS, DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS); // ANNOUNCE_INTERVAL mArpAnnounceIntervalMs = mDependencies.getIntDeviceConfig(ARP_ANNOUNCE_INTERVAL_MS, 20, MAX_ARP_ANNOUNCE_INTERVAL_MS, DEFAULT_ARP_ANNOUNCE_INTERVAL_MS); } private boolean sendArpProbe() { private boolean sendArpProbe() { return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */, return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */, INADDR_ANY /* sender IP */, mHwAddr, mInterfaceBroadcastAddr); INADDR_ANY /* sender IP */, mHwAddr, mInterfaceBroadcastAddr); Loading Loading @@ -1781,7 +1808,8 @@ public class DhcpClient extends StateMachine { return; return; } } mTimeout = mDependencies.getIntDeviceConfig(DHCP_RESTART_CONFIG_DELAY); mTimeout = mDependencies.getIntDeviceConfig(DHCP_RESTART_CONFIG_DELAY, 100, MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS, DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS); super.enter(); super.enter(); sendPacket(); sendPacket(); } } Loading src/android/net/dhcp/DhcpPacket.java +30 −5 Original line number Original line Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.dhcp; package android.net.dhcp; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; Loading @@ -15,6 +31,8 @@ import android.text.TextUtils; import androidx.annotation.Nullable; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting; import com.android.networkstack.apishim.ShimUtils; import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.UnknownHostException; import java.net.UnknownHostException; Loading Loading @@ -350,7 +368,6 @@ public abstract class DhcpPacket { // Set in unit tests, to ensure that the test does not break when run on different devices and // Set in unit tests, to ensure that the test does not break when run on different devices and // on different releases. // on different releases. static String testOverrideVendorId = null; static String testOverrideVendorId = null; static String testOverrideHostname = null; protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, Inet4Address nextIp, Inet4Address relayIp, Inet4Address nextIp, Inet4Address relayIp, Loading Loading @@ -724,10 +741,17 @@ public abstract class DhcpPacket { return "android-dhcp-" + Build.VERSION.RELEASE; return "android-dhcp-" + Build.VERSION.RELEASE; } } private String getHostname() { /** if (testOverrideHostname != null) return testOverrideHostname; * Get the DHCP client hostname after transliteration. */ @VisibleForTesting public String getHostname() { if (mHostName == null && !ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) { return SystemProperties.get("net.hostname"); return SystemProperties.get("net.hostname"); } } return mHostName; } /** /** * Adds common client TLVs. * Adds common client TLVs. Loading Loading @@ -1328,10 +1352,11 @@ public abstract class DhcpPacket { */ */ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, boolean rapidCommit) { boolean rapidCommit, String hostname) { DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit); clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit); pkt.mRequestedParams = expectedParams; pkt.mRequestedParams = expectedParams; pkt.mHostName = hostname; return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); } } Loading src/android/net/util/HostnameTransliterator.java 0 → 100644 +103 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.util; import android.icu.text.Transliterator; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; /** * Transliterator to display a human-readable DHCP client hostname. */ public class HostnameTransliterator { private static final String TAG = "HostnameTransliterator"; // Maximum length of hostname to be encoded in the DHCP message. Following RFC1035#2.3.4 // and this transliterator converts the device name to a single label, so the label length // limit applies to the whole hostname. private static final int MAX_DNS_LABEL_LENGTH = 63; @Nullable private final Transliterator mTransliterator; public HostnameTransliterator() { final Enumeration<String> availableIDs = Transliterator.getAvailableIDs(); final Set<String> actualIds = new HashSet<>(Collections.list(availableIDs)); final StringBuilder rules = new StringBuilder(); if (actualIds.contains("Any-ASCII")) { rules.append(":: Any-ASCII; "); } else if (actualIds.contains("Any-Latin") && actualIds.contains("Latin-ASCII")) { rules.append(":: Any-Latin; :: Latin-ASCII; "); } else { Log.e(TAG, "ICU Transliterator doesn't include supported ID"); mTransliterator = null; return; } mTransliterator = Transliterator.createFromRules("", rules.toString(), Transliterator.FORWARD); } @VisibleForTesting public HostnameTransliterator(Transliterator transliterator) { mTransliterator = transliterator; } // RFC952 and RFC1123 stipulates an valid hostname should be: // 1. Only contain the alphabet (A-Z, a-z), digits (0-9), minus sign (-). // 2. No blank or space characters are permitted as part of a name. // 3. The first character must be an alpha character or digit. // 4. The last character must not be a minus sign (-). private String maybeRemoveRedundantSymbols(@NonNull String string) { String result = string.replaceAll("[^a-zA-Z0-9-]", "-"); result = result.replaceAll("-+", "-"); if (result.startsWith("-")) { result = result.replaceFirst("-", ""); } if (result.endsWith("-")) { result = result.substring(0, result.length() - 1); } return result; } /** * Transliterate the device name to valid hostname that could be human-readable string. */ public String transliterate(@NonNull String deviceName) { if (deviceName == null) return null; if (mTransliterator == null) { if (!deviceName.matches("\\p{ASCII}*")) return null; deviceName = maybeRemoveRedundantSymbols(deviceName); if (TextUtils.isEmpty(deviceName)) return null; return deviceName.length() > MAX_DNS_LABEL_LENGTH ? deviceName.substring(0, MAX_DNS_LABEL_LENGTH) : deviceName; } String hostname = maybeRemoveRedundantSymbols(mTransliterator.transliterate(deviceName)); if (TextUtils.isEmpty(hostname)) return null; return hostname.length() > MAX_DNS_LABEL_LENGTH ? hostname.substring(0, MAX_DNS_LABEL_LENGTH) : hostname; } } Loading
res/values/config.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -46,4 +46,7 @@ <!-- Set to true if NetworkMonitor needs to load the resource by neighbor mcc when device <!-- Set to true if NetworkMonitor needs to load the resource by neighbor mcc when device doesn't have a SIM card inserted. --> doesn't have a SIM card inserted. --> <bool name="config_no_sim_card_uses_neighbor_mcc">false</bool> <bool name="config_no_sim_card_uses_neighbor_mcc">false</bool> <!-- Configuration for including DHCP client hostname option --> <bool name="config_dhcp_client_hostname">false</bool> </resources> </resources>
res/values/overlayable.xml +10 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,16 @@ <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <!-- Configuration value for DhcpResults --> <!-- Configuration value for DhcpResults --> <item type="array" name="config_default_dns_servers"/> <item type="array" name="config_default_dns_servers"/> <!-- Configuration for including DHCP client hostname option. If this option is true, client hostname set in Settings.Global.DEVICE_NAME will be included in DHCPDISCOVER/DHCPREQUEST, otherwise, the DHCP hostname option will not be sent. RFC952 and RFC1123 stipulates an valid hostname should be only comprised of 'a-z', 'A-Z' and '-', and the length should be up to 63 octets or less (RFC1035#2.3.4), platform will perform best-effort transliteration for other characters. Anything that could be used to identify the device uniquely is not recommended, e.g. user's name, random number and etc. --> <item type="bool" name="config_dhcp_client_hostname"/> </policy> </policy> </overlayable> </overlayable> </resources> </resources>
src/android/net/dhcp/DhcpClient.java +88 −60 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.net.ipmemorystore.OnStatusListener; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpConnectivityLog; import android.net.util.HostnameTransliterator; import android.net.util.InterfaceParams; import android.net.util.InterfaceParams; import android.net.util.NetworkStackUtils; import android.net.util.NetworkStackUtils; import android.net.util.PacketReader; import android.net.util.PacketReader; Loading @@ -76,6 +77,7 @@ import android.os.Handler; import android.os.Message; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.ErrnoException; import android.system.Os; import android.system.Os; import android.util.EventLog; import android.util.EventLog; Loading Loading @@ -154,34 +156,40 @@ public class DhcpClient extends StateMachine { // seconds to avoid excessive traffic, but it's too long). // seconds to avoid excessive traffic, but it's too long). @VisibleForTesting @VisibleForTesting public static final String DHCP_RESTART_CONFIG_DELAY = "dhcp_restart_configuration_delay"; public static final String DHCP_RESTART_CONFIG_DELAY = "dhcp_restart_configuration_delay"; private static final int DEFAULT_DHCP_RESTART_CONFIG_DELAY_SEC = 1 * SECONDS; private static final int DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS = 1 * SECONDS; private static final int MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS = 10 * SECONDS; // Initial random delay before sending first ARP probe. // Initial random delay before sending first ARP probe. @VisibleForTesting @VisibleForTesting public static final String ARP_FIRST_PROBE_DELAY_MS = "arp_first_probe_delay"; public static final String ARP_FIRST_PROBE_DELAY_MS = "arp_first_probe_delay"; private static final int DEFAULT_ARP_FIRST_PROBE_DELAY_MS = 100; private static final int DEFAULT_ARP_FIRST_PROBE_DELAY_MS = 100; private static final int MAX_ARP_FIRST_PROBE_DELAY_MS = 1 * SECONDS; // Minimum delay until retransmitting the probe. The probe will be retransmitted after a // Minimum delay until retransmitting the probe. The probe will be retransmitted after a // random number of milliseconds in the range ARP_PROBE_MIN_MS and ARP_PROBE_MAX_MS. // random number of milliseconds in the range ARP_PROBE_MIN_MS and ARP_PROBE_MAX_MS. @VisibleForTesting @VisibleForTesting public static final String ARP_PROBE_MIN_MS = "arp_probe_min"; public static final String ARP_PROBE_MIN_MS = "arp_probe_min"; private static final int DEFAULT_ARP_PROBE_MIN_MS = 100; private static final int DEFAULT_ARP_PROBE_MIN_MS = 100; private static final int MAX_ARP_PROBE_MIN_MS = 1 * SECONDS; // Maximum delay until retransmitting the probe. // Maximum delay until retransmitting the probe. @VisibleForTesting @VisibleForTesting public static final String ARP_PROBE_MAX_MS = "arp_probe_max"; public static final String ARP_PROBE_MAX_MS = "arp_probe_max"; private static final int DEFAULT_ARP_PROBE_MAX_MS = 300; private static final int DEFAULT_ARP_PROBE_MAX_MS = 300; private static final int MAX_ARP_PROBE_MAX_MS = 2 * SECONDS; // Initial random delay before sending first ARP Announcement after completing Probe packet // Initial random delay before sending first ARP Announcement after completing Probe packet // transmission. // transmission. @VisibleForTesting @VisibleForTesting public static final String ARP_FIRST_ANNOUNCE_DELAY_MS = "arp_first_announce_delay"; public static final String ARP_FIRST_ANNOUNCE_DELAY_MS = "arp_first_announce_delay"; private static final int DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS = 100; private static final int DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS = 100; private static final int MAX_ARP_FIRST_ANNOUNCE_DELAY_MS = 2 * SECONDS; // Time between retransmitting ARP Announcement packets. // Time between retransmitting ARP Announcement packets. @VisibleForTesting @VisibleForTesting public static final String ARP_ANNOUNCE_INTERVAL_MS = "arp_announce_interval"; public static final String ARP_ANNOUNCE_INTERVAL_MS = "arp_announce_interval"; private static final int DEFAULT_ARP_ANNOUNCE_INTERVAL_MS = 100; private static final int DEFAULT_ARP_ANNOUNCE_INTERVAL_MS = 100; private static final int MAX_ARP_ANNOUNCE_INTERVAL_MS = 2 * SECONDS; // Max conflict count before configuring interface with declined IP address anyway. // Max conflict count before configuring interface with declined IP address anyway. private static final int MAX_CONFLICTS_COUNT = 2; private static final int MAX_CONFLICTS_COUNT = 2; Loading Loading @@ -306,6 +314,8 @@ public class DhcpClient extends StateMachine { private final NetworkStackIpMemoryStore mIpMemoryStore; private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable @Nullable private DhcpPacketHandler mDhcpPacketHandler; private DhcpPacketHandler mDhcpPacketHandler; @Nullable private final String mHostname; // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. private long mLastInitEnterTime; private long mLastInitEnterTime; Loading Loading @@ -349,6 +359,22 @@ public class DhcpClient extends StateMachine { mNetworkStackIpMemoryStore = store; mNetworkStackIpMemoryStore = store; } } /** * Get the configuration from RRO to check whether or not to send hostname option in * DHCPDISCOVER/DHCPREQUEST message. */ public boolean getSendHostnameOption(final Context context) { return context.getResources().getBoolean(R.bool.config_dhcp_client_hostname); } /** * Get the device name from system settings. */ public String getDeviceName(final Context context) { return Settings.Global.getString(context.getContentResolver(), Settings.Global.DEVICE_NAME); } /** /** * Get a IpMemoryStore instance. * Get a IpMemoryStore instance. */ */ Loading @@ -367,33 +393,10 @@ public class DhcpClient extends StateMachine { /** /** * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace. * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace. */ */ public int getIntDeviceConfig(final String name) { public int getIntDeviceConfig(final String name, int minimumValue, int maximumValue, final int defaultValue; int defaultValue) { switch (name) { return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, case DHCP_RESTART_CONFIG_DELAY: name, minimumValue, maximumValue, defaultValue); defaultValue = DEFAULT_DHCP_RESTART_CONFIG_DELAY_SEC; break; case ARP_FIRST_PROBE_DELAY_MS: defaultValue = DEFAULT_ARP_FIRST_PROBE_DELAY_MS; break; case ARP_PROBE_MIN_MS: defaultValue = DEFAULT_ARP_PROBE_MIN_MS; break; case ARP_PROBE_MAX_MS: defaultValue = DEFAULT_ARP_PROBE_MAX_MS; break; case ARP_FIRST_ANNOUNCE_DELAY_MS: defaultValue = DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS; break; case ARP_ANNOUNCE_INTERVAL_MS: defaultValue = DEFAULT_ARP_ANNOUNCE_INTERVAL_MS; break; default: Log.e(TAG, "Invalid experiment flag: " + name); return 0; } return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, name, defaultValue); } } /** /** Loading Loading @@ -450,6 +453,11 @@ public class DhcpClient extends StateMachine { mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP); mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP); mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP); mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP); // Transliterate hostname read from system settings if RRO option is enabled. final boolean sendHostname = deps.getSendHostnameOption(context); mHostname = sendHostname ? new HostnameTransliterator().transliterate( deps.getDeviceName(mContext)) : null; } } public void registerForPreDhcpNotification() { public void registerForPreDhcpNotification() { Loading Loading @@ -641,7 +649,7 @@ public class DhcpClient extends StateMachine { private boolean sendDiscoverPacket() { private boolean sendDiscoverPacket() { final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled()); DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } } Loading @@ -655,7 +663,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildRequestPacket( final ByteBuffer packet = DhcpPacket.buildRequestPacket( encap, mTransactionId, getSecs(), clientAddress, encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, DO_UNICAST, mHwAddr, requestedAddress, serverAddress, REQUESTED_PARAMS, null); serverAddress, REQUESTED_PARAMS, mHostname); String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + Loading Loading @@ -744,7 +752,7 @@ public class DhcpClient extends StateMachine { mDhcpLease = results; mDhcpLease = results; if (mDhcpLease.dnsServers.isEmpty()) { if (mDhcpLease.dnsServers.isEmpty()) { // supplement customized dns servers // supplement customized dns servers String[] dnsServersList = final String[] dnsServersList = mContext.getResources().getStringArray(R.array.config_default_dns_servers); mContext.getResources().getStringArray(R.array.config_default_dns_servers); for (final String dnsServer : dnsServersList) { for (final String dnsServer : dnsServersList) { try { try { Loading Loading @@ -1259,7 +1267,7 @@ public class DhcpClient extends StateMachine { final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */); DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.payload = packet.array(); l2Packet.payload = packet.array(); Loading Loading @@ -1429,16 +1437,16 @@ public class DhcpClient extends StateMachine { // address being probed for, and the packet's 'sender hardware address' is not the // address being probed for, and the packet's 'sender hardware address' is not the // hardware address of any of the host's interfaces, then the host SHOULD similarly // hardware address of any of the host's interfaces, then the host SHOULD similarly // treat this as an address conflict. // treat this as an address conflict. private boolean checkArpSenderIpOrTargetIp(@NonNull ArpPacket packet, private boolean packetHasIpAddressConflict(@NonNull ArpPacket packet, @NonNull Inet4Address targetIp) { @NonNull Inet4Address targetIp) { return ((!packet.senderIp.equals(INADDR_ANY) && packet.senderIp.equals(targetIp)) return (((!packet.senderIp.equals(INADDR_ANY) && packet.senderIp.equals(targetIp)) || (isArpProbe(packet) && packet.targetIp.equals(targetIp))); || (isArpProbe(packet) && packet.targetIp.equals(targetIp))) && !Arrays.equals(packet.senderHwAddress.toByteArray(), mHwAddr)); } } private boolean hasIpAddressConflict(@NonNull ArpPacket packet, private boolean hasIpAddressConflict(@NonNull ArpPacket packet, @NonNull Inet4Address targetIp) { @NonNull Inet4Address targetIp) { if (checkArpSenderIpOrTargetIp(packet, targetIp) if (!packetHasIpAddressConflict(packet, targetIp)) return false; && !Arrays.equals(packet.senderHwAddress.toByteArray(), mHwAddr)) { if (DBG) { if (DBG) { final String senderIpString = packet.senderIp.getHostAddress(); final String senderIpString = packet.senderIp.getHostAddress(); final String targetIpString = packet.targetIp.getHostAddress(); final String targetIpString = packet.targetIp.getHostAddress(); Loading @@ -1454,8 +1462,6 @@ public class DhcpClient extends StateMachine { } } return true; return true; } } return false; } class IpAddressConflictDetectingState extends LoggingState { class IpAddressConflictDetectingState extends LoggingState { private int mArpProbeCount; private int mArpProbeCount; Loading Loading @@ -1492,13 +1498,7 @@ public class DhcpClient extends StateMachine { } } // Read the customized parameters from DeviceConfig. // Read the customized parameters from DeviceConfig. mArpFirstProbeDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_PROBE_DELAY_MS); readIpConflictParametersFromDeviceConfig(); mArpProbeMaxDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MAX_MS); mArpProbeMinDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MIN_MS); mArpAnnounceIntervalMs = mDependencies.getIntDeviceConfig(ARP_ANNOUNCE_INTERVAL_MS); mArpFirstAnnounceDelayMs = mDependencies.getIntDeviceConfig( ARP_FIRST_ANNOUNCE_DELAY_MS); if (VDBG) { if (VDBG) { Log.d(TAG, "ARP First Probe delay: " + mArpFirstProbeDelayMs Log.d(TAG, "ARP First Probe delay: " + mArpFirstProbeDelayMs + " ARP Probe Max delay: " + mArpProbeMaxDelayMs + " ARP Probe Max delay: " + mArpProbeMaxDelayMs Loading Loading @@ -1577,6 +1577,33 @@ public class DhcpClient extends StateMachine { removeMessages(EVENT_IP_CONFLICT); removeMessages(EVENT_IP_CONFLICT); } } // The following timing parameters are defined in RFC5227 as fixed constants, which // are too long to adopt in the mobile network scenario, however more appropriate to // reference these fixed value as maximumValue argument to restrict the upper bound, // the minimum values of 10/20ms are used to avoid tight loops due to misconfiguration. private void readIpConflictParametersFromDeviceConfig() { // PROBE_WAIT mArpFirstProbeDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_PROBE_DELAY_MS, 10, MAX_ARP_FIRST_PROBE_DELAY_MS, DEFAULT_ARP_FIRST_PROBE_DELAY_MS); // PROBE_MIN mArpProbeMinDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MIN_MS, 10, MAX_ARP_PROBE_MIN_MS, DEFAULT_ARP_PROBE_MIN_MS); // PROBE_MAX mArpProbeMaxDelayMs = Math.max(mArpProbeMinDelayMs + 1, mDependencies.getIntDeviceConfig(ARP_PROBE_MAX_MS, 20, MAX_ARP_PROBE_MAX_MS, DEFAULT_ARP_PROBE_MAX_MS)); // ANNOUNCE_WAIT mArpFirstAnnounceDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_ANNOUNCE_DELAY_MS, 20, MAX_ARP_FIRST_ANNOUNCE_DELAY_MS, DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS); // ANNOUNCE_INTERVAL mArpAnnounceIntervalMs = mDependencies.getIntDeviceConfig(ARP_ANNOUNCE_INTERVAL_MS, 20, MAX_ARP_ANNOUNCE_INTERVAL_MS, DEFAULT_ARP_ANNOUNCE_INTERVAL_MS); } private boolean sendArpProbe() { private boolean sendArpProbe() { return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */, return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */, INADDR_ANY /* sender IP */, mHwAddr, mInterfaceBroadcastAddr); INADDR_ANY /* sender IP */, mHwAddr, mInterfaceBroadcastAddr); Loading Loading @@ -1781,7 +1808,8 @@ public class DhcpClient extends StateMachine { return; return; } } mTimeout = mDependencies.getIntDeviceConfig(DHCP_RESTART_CONFIG_DELAY); mTimeout = mDependencies.getIntDeviceConfig(DHCP_RESTART_CONFIG_DELAY, 100, MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS, DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS); super.enter(); super.enter(); sendPacket(); sendPacket(); } } Loading
src/android/net/dhcp/DhcpPacket.java +30 −5 Original line number Original line Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.dhcp; package android.net.dhcp; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; Loading @@ -15,6 +31,8 @@ import android.text.TextUtils; import androidx.annotation.Nullable; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting; import com.android.networkstack.apishim.ShimUtils; import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.UnknownHostException; import java.net.UnknownHostException; Loading Loading @@ -350,7 +368,6 @@ public abstract class DhcpPacket { // Set in unit tests, to ensure that the test does not break when run on different devices and // Set in unit tests, to ensure that the test does not break when run on different devices and // on different releases. // on different releases. static String testOverrideVendorId = null; static String testOverrideVendorId = null; static String testOverrideHostname = null; protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, Inet4Address nextIp, Inet4Address relayIp, Inet4Address nextIp, Inet4Address relayIp, Loading Loading @@ -724,10 +741,17 @@ public abstract class DhcpPacket { return "android-dhcp-" + Build.VERSION.RELEASE; return "android-dhcp-" + Build.VERSION.RELEASE; } } private String getHostname() { /** if (testOverrideHostname != null) return testOverrideHostname; * Get the DHCP client hostname after transliteration. */ @VisibleForTesting public String getHostname() { if (mHostName == null && !ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) { return SystemProperties.get("net.hostname"); return SystemProperties.get("net.hostname"); } } return mHostName; } /** /** * Adds common client TLVs. * Adds common client TLVs. Loading Loading @@ -1328,10 +1352,11 @@ public abstract class DhcpPacket { */ */ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, boolean rapidCommit) { boolean rapidCommit, String hostname) { DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit); clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit); pkt.mRequestedParams = expectedParams; pkt.mRequestedParams = expectedParams; pkt.mHostName = hostname; return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); } } Loading
src/android/net/util/HostnameTransliterator.java 0 → 100644 +103 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.util; import android.icu.text.Transliterator; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; /** * Transliterator to display a human-readable DHCP client hostname. */ public class HostnameTransliterator { private static final String TAG = "HostnameTransliterator"; // Maximum length of hostname to be encoded in the DHCP message. Following RFC1035#2.3.4 // and this transliterator converts the device name to a single label, so the label length // limit applies to the whole hostname. private static final int MAX_DNS_LABEL_LENGTH = 63; @Nullable private final Transliterator mTransliterator; public HostnameTransliterator() { final Enumeration<String> availableIDs = Transliterator.getAvailableIDs(); final Set<String> actualIds = new HashSet<>(Collections.list(availableIDs)); final StringBuilder rules = new StringBuilder(); if (actualIds.contains("Any-ASCII")) { rules.append(":: Any-ASCII; "); } else if (actualIds.contains("Any-Latin") && actualIds.contains("Latin-ASCII")) { rules.append(":: Any-Latin; :: Latin-ASCII; "); } else { Log.e(TAG, "ICU Transliterator doesn't include supported ID"); mTransliterator = null; return; } mTransliterator = Transliterator.createFromRules("", rules.toString(), Transliterator.FORWARD); } @VisibleForTesting public HostnameTransliterator(Transliterator transliterator) { mTransliterator = transliterator; } // RFC952 and RFC1123 stipulates an valid hostname should be: // 1. Only contain the alphabet (A-Z, a-z), digits (0-9), minus sign (-). // 2. No blank or space characters are permitted as part of a name. // 3. The first character must be an alpha character or digit. // 4. The last character must not be a minus sign (-). private String maybeRemoveRedundantSymbols(@NonNull String string) { String result = string.replaceAll("[^a-zA-Z0-9-]", "-"); result = result.replaceAll("-+", "-"); if (result.startsWith("-")) { result = result.replaceFirst("-", ""); } if (result.endsWith("-")) { result = result.substring(0, result.length() - 1); } return result; } /** * Transliterate the device name to valid hostname that could be human-readable string. */ public String transliterate(@NonNull String deviceName) { if (deviceName == null) return null; if (mTransliterator == null) { if (!deviceName.matches("\\p{ASCII}*")) return null; deviceName = maybeRemoveRedundantSymbols(deviceName); if (TextUtils.isEmpty(deviceName)) return null; return deviceName.length() > MAX_DNS_LABEL_LENGTH ? deviceName.substring(0, MAX_DNS_LABEL_LENGTH) : deviceName; } String hostname = maybeRemoveRedundantSymbols(mTransliterator.transliterate(deviceName)); if (TextUtils.isEmpty(hostname)) return null; return hostname.length() > MAX_DNS_LABEL_LENGTH ? hostname.substring(0, MAX_DNS_LABEL_LENGTH) : hostname; } }