1 /* 2 * Copyright (C) 2014 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 android.net; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.os.Build; 24 import android.os.Parcel; 25 import android.os.ParcelFileDescriptor; 26 import android.os.Parcelable; 27 import android.system.ErrnoException; 28 import android.system.Os; 29 import android.system.OsConstants; 30 31 import com.android.internal.annotations.GuardedBy; 32 33 import libcore.io.IoUtils; 34 import libcore.net.http.Dns; 35 import libcore.net.http.HttpURLConnectionFactory; 36 37 import java.io.FileDescriptor; 38 import java.io.IOException; 39 import java.net.DatagramSocket; 40 import java.net.InetAddress; 41 import java.net.InetSocketAddress; 42 import java.net.MalformedURLException; 43 import java.net.Socket; 44 import java.net.SocketAddress; 45 import java.net.SocketException; 46 import java.net.URL; 47 import java.net.URLConnection; 48 import java.net.UnknownHostException; 49 import java.util.Arrays; 50 import java.util.concurrent.TimeUnit; 51 52 import javax.net.SocketFactory; 53 54 /** 55 * Identifies a {@code Network}. This is supplied to applications via 56 * {@link ConnectivityManager.NetworkCallback} in response to the active 57 * {@link ConnectivityManager#requestNetwork} or passive 58 * {@link ConnectivityManager#registerNetworkCallback} calls. 59 * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis 60 * through a targeted {@link SocketFactory} or process-wide via 61 * {@link ConnectivityManager#bindProcessToNetwork}. 62 */ 63 public class Network implements Parcelable { 64 65 /** 66 * The unique id of the network. 67 * @hide 68 */ 69 @UnsupportedAppUsage 70 public final int netId; 71 72 // Objects used to perform per-network operations such as getSocketFactory 73 // and openConnection, and a lock to protect access to them. 74 private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null; 75 // mUrlConnectionFactory is initialized lazily when it is first needed. 76 @GuardedBy("mLock") 77 private HttpURLConnectionFactory mUrlConnectionFactory; 78 private final Object mLock = new Object(); 79 80 // Default connection pool values. These are evaluated at startup, just 81 // like the OkHttp code. Also like the OkHttp code, we will throw parse 82 // exceptions at class loading time if the properties are set but are not 83 // valid integers. 84 private static final boolean httpKeepAlive = 85 Boolean.parseBoolean(System.getProperty("http.keepAlive", "true")); 86 private static final int httpMaxConnections = 87 httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0; 88 private static final long httpKeepAliveDurationMs = 89 Long.parseLong(System.getProperty("http.keepAliveDuration", "300000")); // 5 minutes. 90 // Value used to obfuscate network handle longs. 91 // The HANDLE_MAGIC value MUST be kept in sync with the corresponding 92 // value in the native/android/net.c NDK implementation. 93 private static final long HANDLE_MAGIC = 0xcafed00dL; 94 private static final int HANDLE_MAGIC_SIZE = 32; 95 private static final int USE_LOCAL_NAMESERVERS_FLAG = 0x80000000; 96 97 // A boolean to control how getAllByName()/getByName() behaves in the face 98 // of Private DNS. 99 // 100 // When true, these calls will request that DNS resolution bypass any 101 // Private DNS that might otherwise apply. Use of this feature is restricted 102 // and permission checks are made by netd (attempts to bypass Private DNS 103 // without appropriate permission are silently turned into vanilla DNS 104 // requests). This only affects DNS queries made using this network object. 105 // 106 // It it not parceled to receivers because (a) it can be set or cleared at 107 // anytime and (b) receivers should be explicit about attempts to bypass 108 // Private DNS so that the intent of the code is easily determined and 109 // code search audits are possible. 110 private final transient boolean mPrivateDnsBypass; 111 112 /** 113 * @hide 114 */ 115 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) Network(int netId)116 public Network(int netId) { 117 this(netId, false); 118 } 119 120 /** 121 * @hide 122 */ Network(int netId, boolean privateDnsBypass)123 public Network(int netId, boolean privateDnsBypass) { 124 this.netId = netId; 125 this.mPrivateDnsBypass = privateDnsBypass; 126 } 127 128 /** 129 * @hide 130 */ 131 @SystemApi Network(@onNull Network that)132 public Network(@NonNull Network that) { 133 this(that.netId, that.mPrivateDnsBypass); 134 } 135 136 /** 137 * Operates the same as {@code InetAddress.getAllByName} except that host 138 * resolution is done on this network. 139 * 140 * @param host the hostname or literal IP string to be resolved. 141 * @return the array of addresses associated with the specified host. 142 * @throws UnknownHostException if the address lookup fails. 143 */ getAllByName(String host)144 public InetAddress[] getAllByName(String host) throws UnknownHostException { 145 return InetAddress.getAllByNameOnNet(host, getNetIdForResolv()); 146 } 147 148 /** 149 * Operates the same as {@code InetAddress.getByName} except that host 150 * resolution is done on this network. 151 * 152 * @param host the hostname to be resolved to an address or {@code null}. 153 * @return the {@code InetAddress} instance representing the host. 154 * @throws UnknownHostException 155 * if the address lookup fails. 156 */ getByName(String host)157 public InetAddress getByName(String host) throws UnknownHostException { 158 return InetAddress.getByNameOnNet(host, getNetIdForResolv()); 159 } 160 161 /** 162 * Obtain a Network object for which Private DNS is to be bypassed when attempting 163 * to use {@link #getAllByName(String)}/{@link #getByName(String)} methods on the given 164 * instance for hostname resolution. 165 * 166 * @hide 167 */ 168 @SystemApi getPrivateDnsBypassingCopy()169 public @NonNull Network getPrivateDnsBypassingCopy() { 170 return new Network(netId, true); 171 } 172 173 /** 174 * Get the unique id of the network. 175 * 176 * @hide 177 */ 178 @SystemApi getNetId()179 public int getNetId() { 180 return netId; 181 } 182 183 /** 184 * Returns a netid marked with the Private DNS bypass flag. 185 * 186 * This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag 187 * in system/netd/include/NetdClient.h. 188 * 189 * @hide 190 */ getNetIdForResolv()191 public int getNetIdForResolv() { 192 return mPrivateDnsBypass 193 ? (USE_LOCAL_NAMESERVERS_FLAG | netId) // Non-portable DNS resolution flag. 194 : netId; 195 } 196 197 /** 198 * A {@code SocketFactory} that produces {@code Socket}'s bound to this network. 199 */ 200 private class NetworkBoundSocketFactory extends SocketFactory { connectToHost(String host, int port, SocketAddress localAddress)201 private Socket connectToHost(String host, int port, SocketAddress localAddress) 202 throws IOException { 203 // Lookup addresses only on this Network. 204 InetAddress[] hostAddresses = getAllByName(host); 205 // Try all addresses. 206 for (int i = 0; i < hostAddresses.length; i++) { 207 try { 208 Socket socket = createSocket(); 209 boolean failed = true; 210 try { 211 if (localAddress != null) socket.bind(localAddress); 212 socket.connect(new InetSocketAddress(hostAddresses[i], port)); 213 failed = false; 214 return socket; 215 } finally { 216 if (failed) IoUtils.closeQuietly(socket); 217 } 218 } catch (IOException e) { 219 if (i == (hostAddresses.length - 1)) throw e; 220 } 221 } 222 throw new UnknownHostException(host); 223 } 224 225 @Override createSocket(String host, int port, InetAddress localHost, int localPort)226 public Socket createSocket(String host, int port, InetAddress localHost, int localPort) 227 throws IOException { 228 return connectToHost(host, port, new InetSocketAddress(localHost, localPort)); 229 } 230 231 @Override createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)232 public Socket createSocket(InetAddress address, int port, InetAddress localAddress, 233 int localPort) throws IOException { 234 Socket socket = createSocket(); 235 boolean failed = true; 236 try { 237 socket.bind(new InetSocketAddress(localAddress, localPort)); 238 socket.connect(new InetSocketAddress(address, port)); 239 failed = false; 240 } finally { 241 if (failed) IoUtils.closeQuietly(socket); 242 } 243 return socket; 244 } 245 246 @Override createSocket(InetAddress host, int port)247 public Socket createSocket(InetAddress host, int port) throws IOException { 248 Socket socket = createSocket(); 249 boolean failed = true; 250 try { 251 socket.connect(new InetSocketAddress(host, port)); 252 failed = false; 253 } finally { 254 if (failed) IoUtils.closeQuietly(socket); 255 } 256 return socket; 257 } 258 259 @Override createSocket(String host, int port)260 public Socket createSocket(String host, int port) throws IOException { 261 return connectToHost(host, port, null); 262 } 263 264 @Override createSocket()265 public Socket createSocket() throws IOException { 266 Socket socket = new Socket(); 267 boolean failed = true; 268 try { 269 bindSocket(socket); 270 failed = false; 271 } finally { 272 if (failed) IoUtils.closeQuietly(socket); 273 } 274 return socket; 275 } 276 } 277 278 /** 279 * Returns a {@link SocketFactory} bound to this network. Any {@link Socket} created by 280 * this factory will have its traffic sent over this {@code Network}. Note that if this 281 * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the 282 * past or future will cease to work. 283 * 284 * @return a {@link SocketFactory} which produces {@link Socket} instances bound to this 285 * {@code Network}. 286 */ getSocketFactory()287 public SocketFactory getSocketFactory() { 288 if (mNetworkBoundSocketFactory == null) { 289 synchronized (mLock) { 290 if (mNetworkBoundSocketFactory == null) { 291 mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(); 292 } 293 } 294 } 295 return mNetworkBoundSocketFactory; 296 } 297 createUrlConnectionFactory(Dns dnsLookup)298 private static HttpURLConnectionFactory createUrlConnectionFactory(Dns dnsLookup) { 299 // Set configuration on the HttpURLConnectionFactory that will be good for all 300 // connections created by this Network. Configuration that might vary is left 301 // until openConnection() and passed as arguments. 302 HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance(); 303 urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup 304 // A private connection pool just for this Network. 305 urlConnectionFactory.setNewConnectionPool(httpMaxConnections, 306 httpKeepAliveDurationMs, TimeUnit.MILLISECONDS); 307 return urlConnectionFactory; 308 } 309 310 /** 311 * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent 312 * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}. 313 * 314 * @return a {@code URLConnection} to the resource referred to by this URL. 315 * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS. 316 * @throws IOException if an error occurs while opening the connection. 317 * @see java.net.URL#openConnection() 318 */ openConnection(URL url)319 public URLConnection openConnection(URL url) throws IOException { 320 final ConnectivityManager cm = ConnectivityManager.getInstanceOrNull(); 321 if (cm == null) { 322 throw new IOException("No ConnectivityManager yet constructed, please construct one"); 323 } 324 // TODO: Should this be optimized to avoid fetching the global proxy for every request? 325 final ProxyInfo proxyInfo = cm.getProxyForNetwork(this); 326 final java.net.Proxy proxy; 327 if (proxyInfo != null) { 328 proxy = proxyInfo.makeProxy(); 329 } else { 330 proxy = java.net.Proxy.NO_PROXY; 331 } 332 return openConnection(url, proxy); 333 } 334 335 /** 336 * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent 337 * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}. 338 * 339 * @param proxy the proxy through which the connection will be established. 340 * @return a {@code URLConnection} to the resource referred to by this URL. 341 * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS. 342 * @throws IllegalArgumentException if the argument proxy is null. 343 * @throws IOException if an error occurs while opening the connection. 344 * @see java.net.URL#openConnection() 345 */ openConnection(URL url, java.net.Proxy proxy)346 public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException { 347 if (proxy == null) throw new IllegalArgumentException("proxy is null"); 348 // TODO: This creates a connection pool and host resolver for 349 // every Network object, instead of one for every NetId. This is 350 // suboptimal, because an app could potentially have more than one 351 // Network object for the same NetId, causing increased memory footprint 352 // and performance penalties due to lack of connection reuse (connection 353 // setup time, congestion window growth time, etc.). 354 // 355 // Instead, investigate only having one connection pool and host resolver 356 // for every NetId, perhaps by using a static HashMap of NetIds to 357 // connection pools and host resolvers. The tricky part is deciding when 358 // to remove a map entry; a WeakHashMap shouldn't be used because whether 359 // a Network is referenced doesn't correlate with whether a new Network 360 // will be instantiated in the near future with the same NetID. A good 361 // solution would involve purging empty (or when all connections are timed 362 // out) ConnectionPools. 363 final HttpURLConnectionFactory urlConnectionFactory; 364 synchronized (mLock) { 365 if (mUrlConnectionFactory == null) { 366 Dns dnsLookup = hostname -> Arrays.asList(getAllByName(hostname)); 367 mUrlConnectionFactory = createUrlConnectionFactory(dnsLookup); 368 } 369 urlConnectionFactory = mUrlConnectionFactory; 370 } 371 SocketFactory socketFactory = getSocketFactory(); 372 return urlConnectionFactory.openConnection(url, socketFactory, proxy); 373 } 374 375 /** 376 * Binds the specified {@link DatagramSocket} to this {@code Network}. All data traffic on the 377 * socket will be sent on this {@code Network}, irrespective of any process-wide network binding 378 * set by {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be 379 * connected. 380 */ bindSocket(DatagramSocket socket)381 public void bindSocket(DatagramSocket socket) throws IOException { 382 // Query a property of the underlying socket to ensure that the socket's file descriptor 383 // exists, is available to bind to a network and is not closed. 384 socket.getReuseAddress(); 385 final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket); 386 bindSocket(pfd.getFileDescriptor()); 387 // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the 388 // dup share the underlying socket in the kernel. The socket is never truly closed until the 389 // last fd pointing to the socket being closed. So close the dup one after binding the 390 // socket to control the lifetime of the dup fd. 391 pfd.close(); 392 } 393 394 /** 395 * Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket 396 * will be sent on this {@code Network}, irrespective of any process-wide network binding set by 397 * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected. 398 */ bindSocket(Socket socket)399 public void bindSocket(Socket socket) throws IOException { 400 // Query a property of the underlying socket to ensure that the socket's file descriptor 401 // exists, is available to bind to a network and is not closed. 402 socket.getReuseAddress(); 403 final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); 404 bindSocket(pfd.getFileDescriptor()); 405 // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the 406 // dup share the underlying socket in the kernel. The socket is never truly closed until the 407 // last fd pointing to the socket being closed. So close the dup one after binding the 408 // socket to control the lifetime of the dup fd. 409 pfd.close(); 410 } 411 412 /** 413 * Binds the specified {@link FileDescriptor} to this {@code Network}. All data traffic on the 414 * socket represented by this file descriptor will be sent on this {@code Network}, 415 * irrespective of any process-wide network binding set by 416 * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected. 417 */ bindSocket(FileDescriptor fd)418 public void bindSocket(FileDescriptor fd) throws IOException { 419 try { 420 final SocketAddress peer = Os.getpeername(fd); 421 final InetAddress inetPeer = ((InetSocketAddress) peer).getAddress(); 422 if (!inetPeer.isAnyLocalAddress()) { 423 // Apparently, the kernel doesn't update a connected UDP socket's 424 // routing upon mark changes. 425 throw new SocketException("Socket is connected"); 426 } 427 } catch (ErrnoException e) { 428 // getpeername() failed. 429 if (e.errno != OsConstants.ENOTCONN) { 430 throw e.rethrowAsSocketException(); 431 } 432 } catch (ClassCastException e) { 433 // Wasn't an InetSocketAddress. 434 throw new SocketException("Only AF_INET/AF_INET6 sockets supported"); 435 } 436 437 final int err = NetworkUtils.bindSocketToNetwork(fd, netId); 438 if (err != 0) { 439 // bindSocketToNetwork returns negative errno. 440 throw new ErrnoException("Binding socket to network " + netId, -err) 441 .rethrowAsSocketException(); 442 } 443 } 444 445 /** 446 * Returns a {@link Network} object given a handle returned from {@link #getNetworkHandle}. 447 * 448 * @param networkHandle a handle returned from {@link #getNetworkHandle}. 449 * @return A {@link Network} object derived from {@code networkHandle}. 450 */ fromNetworkHandle(long networkHandle)451 public static Network fromNetworkHandle(long networkHandle) { 452 if (networkHandle == 0) { 453 throw new IllegalArgumentException( 454 "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network."); 455 } 456 if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC) { 457 throw new IllegalArgumentException( 458 "Value passed to fromNetworkHandle() is not a network handle."); 459 } 460 final int netIdForResolv = (int) (networkHandle >>> HANDLE_MAGIC_SIZE); 461 return new Network((netIdForResolv & ~USE_LOCAL_NAMESERVERS_FLAG), 462 ((netIdForResolv & USE_LOCAL_NAMESERVERS_FLAG) != 0) /* privateDnsBypass */); 463 } 464 465 /** 466 * Returns a handle representing this {@code Network}, for use with the NDK API. 467 */ getNetworkHandle()468 public long getNetworkHandle() { 469 // The network handle is explicitly not the same as the netId. 470 // 471 // The netId is an implementation detail which might be changed in the 472 // future, or which alone (i.e. in the absence of some additional 473 // context) might not be sufficient to fully identify a Network. 474 // 475 // As such, the intention is to prevent accidental misuse of the API 476 // that might result if a developer assumed that handles and netIds 477 // were identical and passing a netId to a call expecting a handle 478 // "just worked". Such accidental misuse, if widely deployed, might 479 // prevent future changes to the semantics of the netId field or 480 // inhibit the expansion of state required for Network objects. 481 // 482 // This extra layer of indirection might be seen as paranoia, and might 483 // never end up being necessary, but the added complexity is trivial. 484 // At some future date it may be desirable to realign the handle with 485 // Multiple Provisioning Domains API recommendations, as made by the 486 // IETF mif working group. 487 if (netId == 0) { 488 return 0L; // make this zero condition obvious for debugging 489 } 490 return (((long) getNetIdForResolv()) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC; 491 } 492 493 // implement the Parcelable interface describeContents()494 public int describeContents() { 495 return 0; 496 } writeToParcel(Parcel dest, int flags)497 public void writeToParcel(Parcel dest, int flags) { 498 dest.writeInt(netId); 499 } 500 501 public static final @android.annotation.NonNull Creator<Network> CREATOR = 502 new Creator<Network>() { 503 public Network createFromParcel(Parcel in) { 504 int netId = in.readInt(); 505 506 return new Network(netId); 507 } 508 509 public Network[] newArray(int size) { 510 return new Network[size]; 511 } 512 }; 513 514 @Override equals(@ullable Object obj)515 public boolean equals(@Nullable Object obj) { 516 if (!(obj instanceof Network)) return false; 517 Network other = (Network)obj; 518 return this.netId == other.netId; 519 } 520 521 @Override hashCode()522 public int hashCode() { 523 return netId * 11; 524 } 525 526 @Override toString()527 public String toString() { 528 return Integer.toString(netId); 529 } 530 } 531