ProxyTlsHandler.kt 5.53 KB
Newer Older
Daniel Wolf's avatar
Daniel Wolf committed
1
2
package com.frostnerd.smokescreen.util.proxy

3
import com.frostnerd.dnstunnelproxy.DnsPacketProxy
4
import com.frostnerd.dnstunnelproxy.IPPacket
5
import com.frostnerd.dnstunnelproxy.Packet
Daniel Wolf's avatar
Daniel Wolf committed
6
7
8
import com.frostnerd.dnstunnelproxy.UpstreamAddress
import com.frostnerd.encrypteddnstunnelproxy.tls.AbstractTLSDnsHandle
import com.frostnerd.encrypteddnstunnelproxy.tls.TLSUpstreamAddress
9
import com.frostnerd.vpntunnelproxy.DeviceWriteToken
Daniel Wolf's avatar
Daniel Wolf committed
10
11
12
import com.frostnerd.vpntunnelproxy.FutureAnswer
import com.frostnerd.vpntunnelproxy.ReceivedAnswer
import org.minidns.dnsmessage.DnsMessage
13
14
15
import org.minidns.record.A
import org.minidns.record.AAAA
import org.minidns.record.Record
Daniel Wolf's avatar
Daniel Wolf committed
16
import java.net.DatagramPacket
17
18
import java.net.Inet4Address
import java.net.Inet6Address
Daniel Wolf's avatar
Daniel Wolf committed
19
import java.net.InetAddress
Daniel Wolf's avatar
Daniel Wolf committed
20
21
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLHandshakeException
Daniel Wolf's avatar
Daniel Wolf committed
22
23
import javax.net.ssl.SSLSession

Daniel Wolf's avatar
Daniel Wolf committed
24
25
/*
 * Copyright (C) 2019 Daniel Wolf (Ch4t4r)
Daniel Wolf's avatar
Daniel Wolf committed
26
 *
Daniel Wolf's avatar
Daniel Wolf committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You can contact the developer at daniel.wolf@frostnerd.com.
Daniel Wolf's avatar
Daniel Wolf committed
41
42
43
 */

class ProxyTlsHandler(
Daniel Wolf's avatar
Daniel Wolf committed
44
    private val ownAddresses:List<String>,
Daniel Wolf's avatar
Daniel Wolf committed
45
    private val upstreamAddresses: List<TLSUpstreamAddress>,
Daniel Wolf's avatar
Daniel Wolf committed
46
    connectTimeout: Int,
Daniel Wolf's avatar
Daniel Wolf committed
47
    val queryCountCallback: (() -> Unit)? = null,
48
    val mapQueryRefusedToHostBlock:Boolean
Daniel Wolf's avatar
Daniel Wolf committed
49
):AbstractTLSDnsHandle(connectTimeout) {
50
51
    override val handlesSpecificRequests: Boolean =
        ProxyBypassHandler.knownSearchDomains.isNotEmpty()
Daniel Wolf's avatar
Daniel Wolf committed
52
    private val hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier()
Daniel Wolf's avatar
Daniel Wolf committed
53
54

    override suspend fun forwardDnsQuestion(
55
        deviceWriteToken: DeviceWriteToken,
Daniel Wolf's avatar
Daniel Wolf committed
56
        dnsMessage: DnsMessage,
57
        originalEnvelope: Packet,
Daniel Wolf's avatar
Daniel Wolf committed
58
59
        realDestination: UpstreamAddress
    ) {
60
61
        val destination = selectAddressOrNull(realDestination)
        if(destination != null) {
62
63
64
65
            if(dnsMessage.questions.all { it.type != null }) {
                val data = dnsMessage.toArray()
                sendPacketToUpstreamDNSServer(deviceWriteToken, DatagramPacket(data, 0, data.size, destination, realDestination.port), originalEnvelope)
            }
66
67
        } else {
            val response = dnsMessage.asBuilder().setQrFlag(true).setResponseCode(DnsMessage.RESPONSE_CODE.SERVER_FAIL)
68
            dnsPacketProxy?.tunnelHandle?.proxy?.logger?.warning("Cannot forward packet because the address isn't resolved yet.")
69
            dnsPacketProxy?.writeUDPDnsPacketToDevice(response.build().toArray(), originalEnvelope)
70
71
72
73
74
75
        }
    }

    private fun selectAddressOrNull(upstreamAddress: UpstreamAddress):InetAddress? {
        val resolveResult = upstreamAddress.addressCreator.resolveOrGetResultOrNull(true) ?: return null
        return resolveResult.firstOrNull {
76
77
            (ipv4Enabled && ipv6Enabled) || (ipv4Enabled && it is Inet4Address) || (ipv6Enabled && it is Inet6Address)
        } ?: (resolveResult.firstOrNull() ?: throw IllegalStateException("The given UpstreamAddress doesn't have an address for the requested IP version (IPv4: $ipv4Enabled, IPv6: $ipv6Enabled)"))
Daniel Wolf's avatar
Daniel Wolf committed
78
79
    }

80
    override fun informFailedRequest(request: FutureAnswer, failureReason:Throwable?) {
Daniel Wolf's avatar
Daniel Wolf committed
81
82
83
    }

    override suspend fun modifyUpstreamResponse(dnsMessage: DnsMessage): DnsMessage {
84
85
86
87
88
89
90
91
        return if(dnsMessage.responseCode == DnsMessage.RESPONSE_CODE.REFUSED) {
            if(dnsMessage.questions.isNotEmpty()) {
                val answer = if(dnsMessage.question.type == Record.TYPE.A) {
                    A("0.0.0.0")
                } else AAAA("::1")
                dnsMessage.asBuilder().setResponseCode(DnsMessage.RESPONSE_CODE.NO_ERROR).addAnswer(Record(dnsMessage.question.name, dnsMessage.question.type, Record.CLASS.IN.value, 50L, answer)).build()
            } else dnsMessage
        } else dnsMessage
Daniel Wolf's avatar
Daniel Wolf committed
92
93
94
    }

    override suspend fun remapDestination(destinationAddress: InetAddress, port: Int): TLSUpstreamAddress {
Daniel Wolf's avatar
Daniel Wolf committed
95
        queryCountCallback?.invoke()
Daniel Wolf's avatar
Daniel Wolf committed
96
97
98
        return upstreamAddresses[0]
    }

99
    override suspend fun shouldHandleDestination(destinationAddress: InetAddress, port: Int): Boolean = ownAddresses.any { it.equals(destinationAddress.hostAddress, true) }
Daniel Wolf's avatar
Daniel Wolf committed
100
101

    override suspend fun shouldHandleRequest(dnsMessage: DnsMessage): Boolean {
102
103
104
105
106
107
        return if(dnsMessage.questions.size > 0) {
            val name = dnsMessage.question.name
            return !ProxyBypassHandler.knownSearchDomains.any {
                name.endsWith(it)
            }
        } else true
Daniel Wolf's avatar
Daniel Wolf committed
108
109
110
    }

    override suspend fun shouldModifyUpstreamResponse(answer: ReceivedAnswer, receivedPayload: ByteArray): Boolean {
111
        return mapQueryRefusedToHostBlock
Daniel Wolf's avatar
Daniel Wolf committed
112
113
114
    }

    override fun verifyConnection(sslSession: SSLSession, outgoingPacket: DatagramPacket) {
Daniel Wolf's avatar
Daniel Wolf committed
115
116
117
        if (!upstreamAddresses.any { hostnameVerifier.verify(it.host, sslSession) }) {
            throw SSLHandshakeException("Certificate mismatch, got ${sslSession.peerPrincipal}, but expected any of ${upstreamAddresses.joinToString { it.host }}")
        }
Daniel Wolf's avatar
Daniel Wolf committed
118
119
120
121
122
123
124
    }

    override fun name(): String {
        return "ProxyTlsHandler"
    }

}