summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/values/config.xml3
-rw-r--r--res/values/overlayable.xml10
-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
-rw-r--r--tests/integration/src/android/net/ip/IpClientIntegrationTest.java111
-rw-r--r--tests/unit/src/android/net/dhcp/DhcpPacketTest.java26
-rw-r--r--tests/unit/src/android/net/util/HostnameTransliteratorTest.java137
8 files changed, 432 insertions, 26 deletions
diff --git a/res/values/config.xml b/res/values/config.xml
index 13ab04e..37a6976 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -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>
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml
index 4727215..57c9e7a 100644
--- a/res/values/overlayable.xml
+++ b/res/values/overlayable.xml
@@ -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>
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;
+ }
+}
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
index 8679a9d..d30b4e9 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
@@ -54,6 +54,7 @@ import static junit.framework.Assert.fail;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
@@ -212,6 +213,8 @@ public class IpClientIntegrationTest {
private static final int TEST_DEFAULT_MTU = 1500;
private static final int TEST_MIN_MTU = 1280;
private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 };
+ private static final String TEST_HOST_NAME = "AOSP on Crosshatch";
+ private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch";
private static class TapPacketReader extends PacketReader {
private final ParcelFileDescriptor mTapFd;
@@ -260,6 +263,8 @@ public class IpClientIntegrationTest {
// Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present.
private HashMap<String, Integer> mIntConfigProperties = new HashMap<>();
private DhcpClient mDhcpClient;
+ private boolean mIsHostnameConfigurationEnabled;
+ private String mHostname;
public void setDhcpLeaseCacheEnabled(final boolean enable) {
mIsDhcpLeaseCacheEnabled = enable;
@@ -273,6 +278,11 @@ public class IpClientIntegrationTest {
mIsDhcpIpConflictDetectEnabled = enable;
}
+ public void setHostnameConfiguration(final boolean enable, final String hostname) {
+ mIsHostnameConfigurationEnabled = enable;
+ mHostname = hostname;
+ }
+
@Override
public INetd getNetd(Context context) {
return mNetd;
@@ -320,6 +330,16 @@ public class IpClientIntegrationTest {
public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) {
return mTimeoutWakeLock;
}
+
+ @Override
+ public boolean getSendHostnameOption(final Context context) {
+ return mIsHostnameConfigurationEnabled;
+ }
+
+ @Override
+ public String getDeviceName(final Context context) {
+ return mIsHostnameConfigurationEnabled ? mHostname : null;
+ }
};
}
@@ -516,7 +536,9 @@ public class IpClientIntegrationTest {
private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled,
- final boolean isDhcpIpConflictDetectEnabled) throws RemoteException {
+ final boolean isDhcpIpConflictDetectEnabled,
+ final boolean isHostnameConfigurationEnabled, final String hostname)
+ throws RemoteException {
ProvisioningConfiguration.Builder builder = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.withoutIPv6();
@@ -525,6 +547,7 @@ public class IpClientIntegrationTest {
mDependencies.setDhcpLeaseCacheEnabled(isDhcpLeaseCacheEnabled);
mDependencies.setDhcpRapidCommitEnabled(shouldReplyRapidCommitAck);
mDependencies.setDhcpIpConflictDetectEnabled(isDhcpIpConflictDetectEnabled);
+ mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled, hostname);
mIpc.setL2KeyAndGroupHint(TEST_L2KEY, TEST_GROUPHINT);
mIpc.startProvisioning(builder.build());
verify(mCb).setNeighborDiscoveryOffload(true);
@@ -534,6 +557,15 @@ public class IpClientIntegrationTest {
verify(mCb, never()).onProvisioningFailure(any());
}
+ private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
+ final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled,
+ final boolean isDhcpIpConflictDetectEnabled)
+ throws RemoteException {
+ startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled,
+ isPreconnectionEnabled, isDhcpIpConflictDetectEnabled,
+ false /* isHostnameConfigurationEnabled */, null /* hostname */);
+ }
+
private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec,
final long startTime, final int mtu) {
final ArgumentCaptor<NetworkAttributes> networkAttributes =
@@ -561,16 +593,33 @@ public class IpClientIntegrationTest {
verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any());
}
+ private void assertHostname(final boolean isHostnameConfigurationEnabled,
+ final String hostname, final String hostnameAfterTransliteration,
+ final List<DhcpPacket> packetList) throws Exception {
+ for (DhcpPacket packet : packetList) {
+ if (!isHostnameConfigurationEnabled || hostname == null) {
+ assertNull(packet.getHostname());
+ } else {
+ assertEquals(packet.getHostname(), hostnameAfterTransliteration);
+ }
+ }
+ }
+
// Helper method to complete DHCP 2-way or 4-way handshake
- private void performDhcpHandshake(final boolean isSuccessLease,
+ private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
final boolean shouldReplyRapidCommitAck, final int mtu,
- final boolean isDhcpIpConflictDetectEnabled) throws Exception {
+ final boolean isDhcpIpConflictDetectEnabled,
+ final boolean isHostnameConfigurationEnabled, final String hostname)
+ throws Exception {
+ final List<DhcpPacket> packetList = new ArrayList<DhcpPacket>();
startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
- false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled);
+ false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
+ isHostnameConfigurationEnabled, hostname);
DhcpPacket packet;
while ((packet = getNextDhcpPacket()) != null) {
+ packetList.add(packet);
if (packet instanceof DhcpDiscoverPacket) {
if (shouldReplyRapidCommitAck) {
sendResponse(buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
@@ -588,9 +637,21 @@ public class IpClientIntegrationTest {
fail("invalid DHCP packet");
}
// wait for reply to DHCPOFFER packet if disabling rapid commit option
- if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) return;
+ if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) {
+ return packetList;
+ }
}
fail("No DHCPREQUEST received on interface");
+ return packetList;
+ }
+
+ private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
+ final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
+ final boolean isDhcpRapidCommitEnabled, final int mtu,
+ final boolean isDhcpIpConflictDetectEnabled) throws Exception {
+ return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled,
+ isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled,
+ false /* isHostnameConfigurationEnabled */, null /* hostname */);
}
private DhcpPacket getNextDhcpPacket() throws ParseException {
@@ -1346,4 +1407,44 @@ public class IpClientIntegrationTest {
true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
+
+ @Test
+ public void testHostname_enableConfig() throws Exception {
+ final long currentTime = System.currentTimeMillis();
+ final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
+ TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
+ false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ false /* isDhcpIpConflictDetectEnabled */,
+ true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */);
+ assertEquals(2, sentPackets.size());
+ assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
+ assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
+ }
+
+ @Test
+ public void testHostname_disableConfig() throws Exception {
+ final long currentTime = System.currentTimeMillis();
+ final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
+ TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
+ false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ false /* isDhcpIpConflictDetectEnabled */,
+ false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME);
+ assertEquals(2, sentPackets.size());
+ assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
+ assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
+ }
+
+ @Test
+ public void testHostname_enableConfigWithNullHostname() throws Exception {
+ final long currentTime = System.currentTimeMillis();
+ final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
+ TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
+ false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
+ false /* isDhcpIpConflictDetectEnabled */,
+ true /* isHostnameConfigurationEnabled */, null /* hostname */);
+ assertEquals(2, sentPackets.size());
+ assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */,
+ sentPackets);
+ assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
+ }
}
diff --git a/tests/unit/src/android/net/dhcp/DhcpPacketTest.java b/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
index fcaf655..090631b 100644
--- a/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
@@ -92,7 +92,6 @@ public class DhcpPacketTest {
@Before
public void setUp() {
DhcpPacket.testOverrideVendorId = "android-dhcp-???";
- DhcpPacket.testOverrideHostname = "android-01234567890abcde";
}
class TestDhcpPacket extends DhcpPacket {
@@ -923,17 +922,19 @@ public class DhcpPacketTest {
@Test
public void testDiscoverPacket() throws Exception {
- short secs = 7;
- int transactionId = 0xdeadbeef;
- byte[] hwaddr = {
+ final short secs = 7;
+ final int transactionId = 0xdeadbeef;
+ final byte[] hwaddr = {
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
};
+ final String testHostname = "android-01234567890abcde";
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
- false /* do unicast */, DhcpClient.REQUESTED_PARAMS, false /* rapid commit */);
+ false /* do unicast */, DhcpClient.REQUESTED_PARAMS, false /* rapid commit */,
+ testHostname);
- byte[] headers = new byte[] {
+ final byte[] headers = new byte[] {
// Ethernet header.
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
@@ -958,7 +959,7 @@ public class DhcpPacketTest {
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
(byte) 0xb1, (byte) 0x7a
};
- byte[] options = new byte[] {
+ final byte[] options = new byte[] {
// Magic cookie 0x63825363.
(byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
// Message type DISCOVER.
@@ -993,16 +994,17 @@ public class DhcpPacketTest {
// Our packets are always of even length. TODO: find out why and possibly fix it.
(byte) 0x00
};
- byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
+ final byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
assertTrue((expected.length & 1) == 0);
+ assertEquals(DhcpPacket.MIN_PACKET_LENGTH_L2,
+ headers.length + 10 /* client hw addr padding */ + 64 /* sname */ + 128 /* file */);
System.arraycopy(headers, 0, expected, 0, headers.length);
System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
- byte[] actual = new byte[packet.limit()];
+ final byte[] actual = new byte[packet.limit()];
packet.get(actual);
- String msg =
- "Expected:\n " + Arrays.toString(expected) +
- "\nActual:\n " + Arrays.toString(actual);
+ String msg = "Expected:\n " + Arrays.toString(expected) + "\nActual:\n "
+ + Arrays.toString(actual);
assertTrue(msg, Arrays.equals(expected, actual));
}
diff --git a/tests/unit/src/android/net/util/HostnameTransliteratorTest.java b/tests/unit/src/android/net/util/HostnameTransliteratorTest.java
new file mode 100644
index 0000000..e6c43d1
--- /dev/null
+++ b/tests/unit/src/android/net/util/HostnameTransliteratorTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for HostnameTransliterator class.
+ *
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HostnameTransliteratorTest {
+ private static final String TEST_HOST_NAME_EN = "AOSP on Crosshatch";
+ private static final String TEST_HOST_NAME_EN_TRANSLITERATION = "AOSP-on-Crosshatch";
+ private static final String TEST_HOST_NAME_JP = "AOSP on ほげほげ";
+ private static final String TEST_HOST_NAME_JP_TRANSLITERATION = "AOSP-on-hogehoge";
+ private static final String TEST_HOST_NAME_CN = "AOSP on 安卓";
+ private static final String TEST_HOST_NAME_CN_TRANSLITERATION = "AOSP-on-an-zhuo";
+ private static final String TEST_HOST_NAME_UNICODE = "AOSP on àéö→ūçŗ̊œ";
+ private static final String TEST_HOST_NAME_UNICODE_TRANSLITERATION = "AOSP-on-aeo-ucroe";
+ private static final String TEST_HOST_NAME_LONG =
+ "AOSP on Ccccccrrrrrroooooosssssshhhhhhaaaaaattttttcccccchhhhhh3abcd";
+ private static final String TEST_HOST_NAME_LONG_TRANSLITERATION =
+ "AOSP-on-Ccccccrrrrrroooooosssssshhhhhhaaaaaattttttcccccchhhhhh3";
+ private static final String TEST_HOST_NAME_LONG_TRUNCATED =
+ "AOSP-on-Ccccccrrrrrroooooosssssshhhhhhaaaaaattttttcccccchhhhhh3";
+
+ @NonNull
+ private HostnameTransliterator mTransliterator;
+
+ @Before
+ public void setUp() throws Exception {
+ mTransliterator = new HostnameTransliterator();
+ assertNotNull(mTransliterator);
+ }
+
+ @Test
+ public void testNullHostname() {
+ assertNull(mTransliterator.transliterate(null));
+ }
+
+ private void assertHostnameTransliteration(final String hostnameAftertransliteration,
+ final String hostname) {
+ assertEquals(hostnameAftertransliteration, mTransliterator.transliterate(hostname));
+ }
+
+ @Test
+ public void testEmptyHostname() {
+ assertHostnameTransliteration(null, "");
+ }
+
+ @Test
+ public void testHostnameOnlyTabs() {
+ assertHostnameTransliteration(null, "\t\t");
+ }
+
+ @Test
+ public void testHostnameOnlySpaces() {
+ assertHostnameTransliteration(null, " ");
+ }
+
+ @Test
+ public void testHostnameOnlyUnsupportedAsciiSymbols() {
+ final String symbol = new String(new byte[] { 0x00, 0x1b /* ESC */, 0x7f /* DEL */,
+ 0x10 /* backspace */}, StandardCharsets.US_ASCII);
+ assertHostnameTransliteration(null, symbol);
+ }
+
+ @Test
+ public void testHostnameMixedAsciiSymbols() {
+ final String symbol = new String(new byte[] { 0x00, 'a', 0x1b /* ESC */, 0x7f /* DEL */,
+ 'b', 0x10 /* backspace */}, StandardCharsets.US_ASCII);
+ assertHostnameTransliteration("a-b", symbol);
+ }
+
+ @Test
+ public void testHostnames() {
+ assertHostnameTransliteration(TEST_HOST_NAME_EN_TRANSLITERATION, TEST_HOST_NAME_EN);
+ assertHostnameTransliteration(TEST_HOST_NAME_JP_TRANSLITERATION, TEST_HOST_NAME_JP);
+ assertHostnameTransliteration(TEST_HOST_NAME_CN_TRANSLITERATION, TEST_HOST_NAME_CN);
+ assertHostnameTransliteration(TEST_HOST_NAME_UNICODE_TRANSLITERATION,
+ TEST_HOST_NAME_UNICODE);
+ }
+
+ @Test
+ public void testHostnameWithMinusSign() {
+ assertHostnameTransliteration(TEST_HOST_NAME_EN_TRANSLITERATION, "-AOSP on Crosshatch");
+ assertHostnameTransliteration(TEST_HOST_NAME_EN_TRANSLITERATION, "AOSP on Crosshatch-");
+ assertHostnameTransliteration(TEST_HOST_NAME_EN_TRANSLITERATION, "---AOSP on Crosshatch");
+ assertHostnameTransliteration(TEST_HOST_NAME_EN_TRANSLITERATION, "AOSP on Crosshatch---");
+ assertHostnameTransliteration(TEST_HOST_NAME_EN_TRANSLITERATION, "AOSP---on---Crosshatch");
+ }
+
+ @Test
+ public void testLongHostname() {
+ assertHostnameTransliteration(TEST_HOST_NAME_LONG_TRANSLITERATION, TEST_HOST_NAME_LONG);
+ }
+
+ @Test
+ public void testNonAsciiHostname() {
+ mTransliterator = new HostnameTransliterator(null);
+ assertHostnameTransliteration(null, TEST_HOST_NAME_UNICODE);
+ }
+
+ @Test
+ public void testAsciiLongHostname() {
+ mTransliterator = new HostnameTransliterator(null);
+ assertHostnameTransliteration(TEST_HOST_NAME_LONG_TRUNCATED, TEST_HOST_NAME_LONG);
+ }
+}