简体   繁体   中英

How do I override the DNS resolution of an Android WebView?

I have a webview that loads up content from one of two datacenters, one prod and one gamma. Both data centers have the same certificate for the same domain. The domain usually just cnames to the prod data center.

For desktop web browser testing, people override /etc/hosts or use browser extensions to point to the gamma datacenters IPs for the domain.

I want to alter the DNS resolution of the webview in our android app so that my gamma data center is used (based on an application setting).

So basically I want something like shouldOverrideUrlLoading but instead of overriding the URL, I want to override the DNS Resolution / IPs that are being connected to . I don't want to provide a different URL / domain name because then the cert will not be valid.

It isn't an option to alter the datacenter/cert set up as many other systems already depend on it.

Little late but you can do this by intercepting and resolving the WebView request by yourself.

You'll need to use OkHttp's DNS Over Https (DOH)

Steps:

  1. Configure OkHttpClient with a Custom Dns Resolver.
  2. Intercept the Webview Request and then use the OkHttpClient to make a new request using the Webview request Url and Headers.
  3. Build a WebResourceResoponse Object with the response received from OkHttp and return it to the Webview.

Working Example

Dependencies:

implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps: 4.9.0'

Your WebviewClient:

// returning null =  WebView will handle the request normally
     override fun shouldInterceptRequest(
            view: WebView?,
            request: WebResourceRequest?
        ): WebResourceResponse? {
        return try {
            when(request?.url.toString()){
                "<YOUR_URL>" -> WebViewNetworkHandler(request)
                else -> null
            }
        }
            } catch(e: Exception) {
                e.printStackTrace();
                null
            }
        }

Your WebViewNetworkHandler:

object WebViewNetworkHandler {
    private var client: OkHttpClient? = null

    operator fun invoke(webResourceRequest: WebResourceRequest?): WebResourceResponse? {
        val url: String = webResourceRequest?.url.toString()
        val headers: Headers? = webResourceRequest?.requestHeaders?.toHeaders()

        val newRequest = headers?.let {
            Request.Builder()
                .url(url)
                .headers(it)
                .build()
        }

        val response = newRequest?.let { getClient().newCall(newRequest).execute() }


        return response?.let {
            WebResourceResponse(
                it.body?.contentType()?.let { "${it.type}/${it.subtype}" },
                it.body?.contentType()?.charset(Charset.defaultCharset())?.name(),
                it.code,
                "OK",
                it.headers.toMap(),
                it.body?.byteStream()
            )
        }
    }

    private fun getClient() = client ?: makeClient().also { client = it }

    private fun makeClient(): OkHttpClient {

        val appCache = Cache(File("cacheDir", "okhttpcache"), 10 * 1024 * 1024)

        val bootstrapClient = OkHttpClient.Builder()
            .cache(appCache)
            .build()

        return bootstrapClient.newBuilder()
            .dns(CustomDns)
            .build()
    }

    object CustomDns: Dns {
        override fun lookup(hostname: String): List<InetAddress> {
            return try {
                InetAddress.getAllByName("<YOUR_IP>").toList()
            } catch(e: Exception){
                throw UnknownHostException("Failed dns lookup of $hostname").apply {
                    initCause(e)
                }
            }
        }
    }
}

Haven't tested it yet but it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM