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

Commit 8926b4c9 authored by Xiao Ma's avatar Xiao Ma Committed by Gerrit Code Review
Browse files

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

parents 609972ba 8bcd6735
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