Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38d0f87f authored by Xiao Ma's avatar Xiao Ma Committed by android-build-merger
Browse files

Merge "Provide RRO configuration to send DHCP client hostname option."

am: 8926b4c9

Change-Id: Id6a3cdb1d08d934e311e9791ca84ef00359871ef
parents 18e994fc 8926b4c9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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>
+10 −0
Original line number Diff line number Diff line
@@ -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>
+29 −4
Original line number Diff line number Diff line
@@ -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;
@@ -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.
         */
@@ -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();
+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;
@@ -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,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.
@@ -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);
    }

+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