diff options
author | Xiao Ma <xiaom@google.com> | 2019-12-27 09:32:41 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-12-27 09:32:41 +0000 |
commit | 8926b4c96d986d8478ce82a5d281ef81e97d75d6 (patch) | |
tree | e998554bef27ee7a2ec4a7e5dc6801205ab19260 /src | |
parent | 609972ba1d3795fa14970a0522e146ba6427d4a3 (diff) | |
parent | 8bcd6735907353caa493d232f01a3fa191bc2f4c (diff) |
Merge "Provide RRO configuration to send DHCP client hostname option."
Diffstat (limited to 'src')
-rw-r--r-- | src/android/net/dhcp/DhcpClient.java | 33 | ||||
-rw-r--r-- | src/android/net/dhcp/DhcpPacket.java | 35 | ||||
-rw-r--r-- | src/android/net/util/HostnameTransliterator.java | 103 |
3 files changed, 162 insertions, 9 deletions
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index 662a640..8aa739a 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java @@ -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; @@ -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; @@ -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; @@ -356,6 +360,22 @@ public class DhcpClient extends StateMachine { } /** + * 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. */ public NetworkStackIpMemoryStore getIpMemoryStore() { @@ -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() { @@ -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); } @@ -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() + @@ -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 { @@ -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(); diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java index c5700b3..9eca0b5 100644 --- a/src/android/net/dhcp/DhcpPacket.java +++ b/src/android/net/dhcp/DhcpPacket.java @@ -1,3 +1,19 @@ +/* + * 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; @@ -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; @@ -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, @@ -724,9 +741,16 @@ public abstract class DhcpPacket { return "android-dhcp-" + Build.VERSION.RELEASE; } - private String getHostname() { - if (testOverrideHostname != null) return testOverrideHostname; - return SystemProperties.get("net.hostname"); + /** + * 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; } /** @@ -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); } diff --git a/src/android/net/util/HostnameTransliterator.java b/src/android/net/util/HostnameTransliterator.java new file mode 100644 index 0000000..cf126d1 --- /dev/null +++ b/src/android/net/util/HostnameTransliterator.java @@ -0,0 +1,103 @@ +/* + * 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; + } +} |