Loading res/values/config.xml +3 −0 Original line number 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 doesn't have a SIM card inserted. --> <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> res/values/overlayable.xml +10 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,16 @@ <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <!-- Configuration value for DhcpResults --> <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> </overlayable> </resources> src/android/net/dhcp/DhcpClient.java +29 −4 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.net.ipmemorystore.OnStatusListener; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.IpConnectivityLog; import android.net.util.HostnameTransliterator; import android.net.util.InterfaceParams; import android.net.util.NetworkStackUtils; import android.net.util.PacketReader; Loading @@ -76,6 +77,7 @@ import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.Os; import android.util.EventLog; Loading Loading @@ -312,6 +314,8 @@ public class DhcpClient extends StateMachine { private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable private DhcpPacketHandler mDhcpPacketHandler; @Nullable private final String mHostname; // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. private long mLastInitEnterTime; Loading Loading @@ -355,6 +359,22 @@ public class DhcpClient extends StateMachine { 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. */ Loading Loading @@ -433,6 +453,11 @@ public class DhcpClient extends StateMachine { mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_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() { Loading Loading @@ -624,7 +649,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()); DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } Loading @@ -638,7 +663,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildRequestPacket( encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, serverAddress, REQUESTED_PARAMS, null); serverAddress, REQUESTED_PARAMS, mHostname); String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + Loading Loading @@ -727,7 +752,7 @@ public class DhcpClient extends StateMachine { mDhcpLease = results; if (mDhcpLease.dnsServers.isEmpty()) { // supplement customized dns servers String[] dnsServersList = final String[] dnsServersList = mContext.getResources().getStringArray(R.array.config_default_dns_servers); for (final String dnsServer : dnsServersList) { try { Loading Loading @@ -1242,7 +1267,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 */); DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.payload = packet.array(); Loading src/android/net/dhcp/DhcpPacket.java +30 −5 Original line number 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; 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.VisibleForTesting; import com.android.networkstack.apishim.ShimUtils; import java.io.UnsupportedEncodingException; import java.net.Inet4Address; 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 // on different releases. static String testOverrideVendorId = null; static String testOverrideHostname = null; protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, Inet4Address nextIp, Inet4Address relayIp, Loading Loading @@ -724,10 +741,17 @@ public abstract class DhcpPacket { 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 mHostName; } /** * Adds common client TLVs. Loading Loading @@ -1328,10 +1352,11 @@ public abstract class DhcpPacket { */ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, boolean rapidCommit) { boolean rapidCommit, String hostname) { DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit); pkt.mRequestedParams = expectedParams; pkt.mHostName = hostname; return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); } Loading src/android/net/util/HostnameTransliterator.java 0 → 100644 +103 −0 Original line number 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 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 doesn't have a SIM card inserted. --> <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>
res/values/overlayable.xml +10 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,16 @@ <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <!-- Configuration value for DhcpResults --> <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> </overlayable> </resources>
src/android/net/dhcp/DhcpClient.java +29 −4 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.net.ipmemorystore.OnStatusListener; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.IpConnectivityLog; import android.net.util.HostnameTransliterator; import android.net.util.InterfaceParams; import android.net.util.NetworkStackUtils; import android.net.util.PacketReader; Loading @@ -76,6 +77,7 @@ import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.Os; import android.util.EventLog; Loading Loading @@ -312,6 +314,8 @@ public class DhcpClient extends StateMachine { private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable private DhcpPacketHandler mDhcpPacketHandler; @Nullable private final String mHostname; // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. private long mLastInitEnterTime; Loading Loading @@ -355,6 +359,22 @@ public class DhcpClient extends StateMachine { 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. */ Loading Loading @@ -433,6 +453,11 @@ public class DhcpClient extends StateMachine { mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_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() { Loading Loading @@ -624,7 +649,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()); DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } Loading @@ -638,7 +663,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildRequestPacket( encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, serverAddress, REQUESTED_PARAMS, null); serverAddress, REQUESTED_PARAMS, mHostname); String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + Loading Loading @@ -727,7 +752,7 @@ public class DhcpClient extends StateMachine { mDhcpLease = results; if (mDhcpLease.dnsServers.isEmpty()) { // supplement customized dns servers String[] dnsServersList = final String[] dnsServersList = mContext.getResources().getStringArray(R.array.config_default_dns_servers); for (final String dnsServer : dnsServersList) { try { Loading Loading @@ -1242,7 +1267,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 */); DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.payload = packet.array(); Loading
src/android/net/dhcp/DhcpPacket.java +30 −5 Original line number 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; 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.VisibleForTesting; import com.android.networkstack.apishim.ShimUtils; import java.io.UnsupportedEncodingException; import java.net.Inet4Address; 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 // on different releases. static String testOverrideVendorId = null; static String testOverrideHostname = null; protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, Inet4Address nextIp, Inet4Address relayIp, Loading Loading @@ -724,10 +741,17 @@ public abstract class DhcpPacket { 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 mHostName; } /** * Adds common client TLVs. Loading Loading @@ -1328,10 +1352,11 @@ public abstract class DhcpPacket { */ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, boolean rapidCommit) { boolean rapidCommit, String hostname) { DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit); pkt.mRequestedParams = expectedParams; pkt.mHostName = hostname; return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); } Loading
src/android/net/util/HostnameTransliterator.java 0 → 100644 +103 −0 Original line number 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; } }