summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/net/dhcp/DhcpClient.java33
-rw-r--r--src/android/net/dhcp/DhcpPacket.java35
-rw-r--r--src/android/net/util/HostnameTransliterator.java103
3 files changed, 162 insertions, 9 deletions
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index 982d8ce..b1df996 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;
@@ -306,6 +308,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;
@@ -350,6 +354,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() {
@@ -450,6 +470,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() {
@@ -641,7 +666,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);
}
@@ -655,7 +680,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() +
@@ -744,7 +769,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 {
@@ -1259,7 +1284,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;
+ }
+}