Android 4.4.4 -Andoird 5.0.0代理(ProxySlector 中select函数)导致的BUG分析

Android 4.4.4 发展到 Andoird 5.0.0 其中 OKhttp 中的一点点变化,导致的一个问题:
在使用PRoxySelector 中的select函数返回proxy ,构造proxy如果使用的resolve的方式构造,
在而后的网络请求中会经历如下过程:
1、host 解析成 InetAddress
2、InetAddress反向解析成name
3、然后将name正向解析成InetAddress
过程及其繁琐,导致请求的效率下降,也可能导致错误。
什么样的错误呢?
例如先给ip:
1、1.2.3.4,
2、逆向解析成 :4.3.2.1.broad.wh.hb.dynamic.163data.com.cn
3、正向解析出错 public1.114dns.com can't find 4.3.2.1.broad.wh.hb.dynamic.163data.com.cn: Non-existent domain

仔细分析原因后发现,Android 5.0.0以下的网络请求使用OKhttp,然后里面有一个比较关键的类RouteSelector。其中有一段代码是这样的:

212
  private void resetNextInetSocketAddress(Proxy proxy) throws UnknownHostException {
213
    socketAddresses = null; // Clear the addresses. Necessary if getAllByName() below throws!
214
 
  
215
    String socketHost;
216
    if (proxy.type() == Proxy.Type.DIRECT) {
217
      socketHost = uri.getHost();
218
      socketPort = getEffectivePort(uri);
219
    } else {
220
      SocketAddress proxyAddress = proxy.address();
221
      if (!(proxyAddress instanceof InetSocketAddress)) {
222
        throw new IllegalArgumentException(
223
            "Proxy.address() is not an " + "InetSocketAddress: " + proxyAddress.getClass());
224
      }
225
      InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
226
      socketHost = proxySocketAddress.getHostName();
227
      socketPort = proxySocketAddress.getPort();
228
    }
229
 
  
230
    // Try each address for best behavior in mixed IPv4/IPv6 environments.
231
    socketAddresses = dns.getAllByName(socketHost);
232
    nextSocketAddressIndex = 0;
233
  }



public class InetAddress implements Serializable {

    private static InetAddress getHostByAddrImpl(InetAddress address) throws UnknownHostException {
        BlockGuard.getThreadPolicy().onNetwork();
        try {
           String hostname = Libcore.os.getnameinfo(address, NI_NAMEREQD);// 如果找不到主机名字, 将其作为一个错误对待.
            return makeInetAddress(address. ipaddress .clone(), hostname);
        } catch (GaiException gaiException) {
            throw gaiException.rethrowAsUnknownHostException();
        }
    }


    public String getHostName() {
        if ( hostName == null ) {
            try {
                hostName= getHostByAddrImpl(this).hostName;
            } catch (UnknownHostException ex) {
                hostName = getHostAddress();
            }
        }
        return hostName ;
    }


    /**
     * Returns the numeric representation of this IP address (such as "127.0.0.1").
     */
    public String getHostAddress() {
        return Libcore.os.getnameinfo( this , NI_NUMERICHOST); // Can't throw.以数字形式而非名字返回主机地址
    }
}

  • NI_DGRAM: 服务基于数据报而非基于流.
  • NI_NAMEREQD: 如果找不到主机名字, 将其作为一个错误对待.
  • NI_NOFQDN: 对于本地主机, 仅返回完全限定域名的节点名部分.
  • NI_NUMERICHOST: 以数字形式而非名字返回主机地址.
  • NI_NUMERICSERV: 以数字形式而非名字返回服务地址(即端口号).


其中代码段:
226
      socketHost = proxySocketAddress.getHostName();
获取到的结果会是一个通过逆向解析获取ip对应的域名,如果找不到主机名字, 将其作为一个异常抛出.然后使用IP作为host。


按照正常逻辑 从IP反向解析域名能通的话,应该这个域名也能够解析成IP!!!
但是现实生活中不是这样的,可能我们活在中国奇葩的网络中吧。

还好在Andorid 5.0.0以后版本中的OKhttp使用已经切换如下代码了:
245
  private void resetNextInetSocketAddress(Proxy proxy) throws UnknownHostException {
246
    // Clear the addresses. Necessary if getAllByName() below throws!
247
    inetSocketAddresses = new ArrayList<>();
248
 
       
249
    String socketHost;
250
    int socketPort;
251
    if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
252
      socketHost = address.getUriHost();
253
      socketPort = getEffectivePort(uri);
254
    } else {
255
      SocketAddress proxyAddress = proxy.address();
256
      if (!(proxyAddress instanceof InetSocketAddress)) {
257
        throw new IllegalArgumentException(
258
            "Proxy.address() is not an " + "InetSocketAddress: " + proxyAddress.getClass());
259
      }
260
      InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
261
      socketHost = getHostString(proxySocketAddress);
262
      socketPort = proxySocketAddress.getPort();
263
    }
264
 
       
265
    // Try each address for best behavior in mixed IPv4/IPv6 environments.
266
    for (InetAddress inetAddress : network.resolveInetAddresses(socketHost)) {
267
      inetSocketAddresses.add(new InetSocketAddress(inetAddress, socketPort));
268
    }
269
    nextInetSocketAddressIndex = 0;
270
  }



Obtain a "host" from an  java.net.InetSocketAddress. This returns a string containing either an actual host name or a numeric IP address.
275
 
  
276
  // Visible for testing
277
  static String getHostString(InetSocketAddress socketAddress) {
278
    InetAddress address = socketAddress.getAddress();
279
    if (address == null) {
280
      // The InetSocketAddress was specified with a string (either a numeric IP or a host name). If
281
      // it is a name, all IPs for that name should be tried. If it is an IP address, only that IP
282
      // address should be tried.
283
      return socketAddress.getHostName();
284
    }
285
    // The InetSocketAddress has a specific address: we should only try that address. Therefore we
286
    // return the address and ignore any host name that may be available.
287
    return address.getHostAddress();
288
  }
这样就能很好的解决这个问题,不存在什么反向解析 然后正向解析的问题了。但是我们Android5.0一下的操作系统怎么办呢????
哈哈 Android 源代码管理的官网上已经将okhttp这个bug 同步过去,bug 号码是#1186
https://github.com/square/okhttp/pull/1186
其实我们创建代理的时候使用不解析的方式创建就可以避免上面的问题!!










https://android.googlesource.com/platform/external/okhttp/+log/android-5.1.0_r1/
https://android.googlesource.com/platform/external/okhttp/+log/android-5.1.0_r1/okhttp/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java


android /  platform /  external /  okhttp /  android-5.1.0_r1 /  okhttp /  src /  main /  java /  com /  squareup /  okhttp / internal /  http /  RouteSelector.java
tag d3c92892dd20b7362fe5039f99a0c49304425e30  
tagger The Android Open Source Project Mon Mar 02 08:26:28 2015 -0800
object be40c8024c0572878c55d2e032f3cbb6ef85ccd9  
Android 5.1.0 release 1
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iEYEABECAAYFAlT0jzQACgkQ6K0/gZqxDnhW0ACeIsx37SoxX66T/Vx/uThDRCLQ
zCQAoJE9d0p9nbad8JBPOEC8OJ/32quz
=aQm/
-----END PGP SIGNATURE-----
  1. 2ed0095 Avoid a reverse DNS-lookup for a numeric proxy address by Neil Fuller · 1 year ago
  2. cc168fe Be consistent about host names in RouteSelector. by Narayan Kamath · 1 year, 2 months ago
  3. 7c7f22d Allow callers to pass in a custom host resolver implementation. by Lorenzo Colitti · 1 year, 5 months ago
  4. 3c938a3 Update okhttp to a more recent commit. by Neil Fuller · 1 year, 10 months ago
  5. 166772b Update okhttp. by Narayan Kamath · 2 years, 1 month ago


https://github.com/square/okhttp/pull/1186


你可能感兴趣的:(WR_ANDROID)