1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.net.module.util;
18 
19 import android.annotation.NonNull;
20 import android.os.Parcel;
21 import android.util.Log;
22 
23 
24 import java.net.Inet4Address;
25 import java.net.Inet6Address;
26 import java.net.InetAddress;
27 import java.net.UnknownHostException;
28 
29 /**
30  * Collection of utilities to interact with {@link InetAddress}
31  * @hide
32  */
33 public class InetAddressUtils {
34 
35     private static final String TAG = InetAddressUtils.class.getSimpleName();
36     private static final int INET4_ADDR_LENGTH = 4;
37     private static final int INET6_ADDR_LENGTH = 16;
38 
39     /**
40      * Writes an InetAddress to a parcel. The address may be null. This is likely faster than
41      * calling writeSerializable.
42      * @hide
43      */
parcelInetAddress(Parcel parcel, InetAddress address, int flags)44     public static void parcelInetAddress(Parcel parcel, InetAddress address, int flags) {
45         byte[] addressArray = (address != null) ? address.getAddress() : null;
46         parcel.writeByteArray(addressArray);
47         if (address instanceof Inet6Address) {
48             final Inet6Address v6Address = (Inet6Address) address;
49             final boolean hasScopeId = v6Address.getScopeId() != 0;
50             parcel.writeBoolean(hasScopeId);
51             if (hasScopeId) parcel.writeInt(v6Address.getScopeId());
52         }
53 
54     }
55 
56     /**
57      * Reads an InetAddress from a parcel. Returns null if the address that was written was null
58      * or if the data is invalid.
59      * @hide
60      */
unparcelInetAddress(Parcel in)61     public static InetAddress unparcelInetAddress(Parcel in) {
62         byte[] addressArray = in.createByteArray();
63         if (addressArray == null) {
64             return null;
65         }
66 
67         try {
68             if (addressArray.length == INET6_ADDR_LENGTH) {
69                 final boolean hasScopeId = in.readBoolean();
70                 final int scopeId = hasScopeId ? in.readInt() : 0;
71                 return Inet6Address.getByAddress(null /* host */, addressArray, scopeId);
72             }
73 
74             return InetAddress.getByAddress(addressArray);
75         } catch (UnknownHostException e) {
76             return null;
77         }
78     }
79 
80     /**
81      * Create a Inet6Address with scope id if it is a link local address. Otherwise, returns the
82      * original address.
83      */
withScopeId(@onNull final Inet6Address addr, int scopeid)84     public static Inet6Address withScopeId(@NonNull final Inet6Address addr, int scopeid) {
85         if (!addr.isLinkLocalAddress()) {
86             return addr;
87         }
88         try {
89             return Inet6Address.getByAddress(null /* host */, addr.getAddress(),
90                     scopeid);
91         } catch (UnknownHostException impossible) {
92             Log.wtf(TAG, "Cannot construct scoped Inet6Address with Inet6Address.getAddress("
93                     + addr.getHostAddress() + "): ", impossible);
94             return null;
95         }
96     }
97 
98     /**
99      * Create a v4-mapped v6 address from v4 address
100      *
101      * @param v4Addr Inet4Address which is converted to v4-mapped v6 address
102      * @return v4-mapped v6 address
103      */
v4MappedV6Address(@onNull final Inet4Address v4Addr)104     public static Inet6Address v4MappedV6Address(@NonNull final Inet4Address v4Addr) {
105         final byte[] v6AddrBytes = new byte[INET6_ADDR_LENGTH];
106         v6AddrBytes[10] = (byte) 0xFF;
107         v6AddrBytes[11] = (byte) 0xFF;
108         System.arraycopy(v4Addr.getAddress(), 0 /* srcPos */, v6AddrBytes, 12 /* dstPos */,
109                 INET4_ADDR_LENGTH);
110         try {
111             // Using Inet6Address.getByAddress since InetAddress.getByAddress converts v4-mapped v6
112             // address to v4 address internally and returns Inet4Address
113             return Inet6Address.getByAddress(null /* host */, v6AddrBytes, -1 /* scope_id */);
114         } catch (UnknownHostException impossible) {
115             // getByAddress throws UnknownHostException when the argument is the invalid length
116             // but INET6_ADDR_LENGTH(16) is the valid length.
117             Log.wtf(TAG, "Failed to generate v4-mapped v6 address from " + v4Addr, impossible);
118             return null;
119         }
120     }
121 
InetAddressUtils()122     private InetAddressUtils() {}
123 }
124