aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/main/java/com/wireguard/android/util/ErrorMessages.kt
blob: d8ac94d990d9fa3ba3e50443fdc782598f8b1b38 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
package com.wireguard.android.util

import android.content.res.Resources
import android.os.RemoteException
import com.wireguard.android.Application.Companion.get
import com.wireguard.android.R
import com.wireguard.android.backend.BackendException
import com.wireguard.android.util.RootShell.RootShellException
import com.wireguard.config.BadConfigException
import com.wireguard.config.InetEndpoint
import com.wireguard.config.InetNetwork
import com.wireguard.config.ParseException
import com.wireguard.crypto.Key
import com.wireguard.crypto.KeyFormatException
import java.net.InetAddress

object ErrorMessages {
    private val BCE_REASON_MAP = mapOf(
            BadConfigException.Reason.INVALID_KEY to R.string.bad_config_reason_invalid_key,
            BadConfigException.Reason.INVALID_NUMBER to R.string.bad_config_reason_invalid_number,
            BadConfigException.Reason.INVALID_VALUE to R.string.bad_config_reason_invalid_value,
            BadConfigException.Reason.MISSING_ATTRIBUTE to R.string.bad_config_reason_missing_attribute,
            BadConfigException.Reason.MISSING_SECTION to R.string.bad_config_reason_missing_section,
            BadConfigException.Reason.SYNTAX_ERROR to R.string.bad_config_reason_syntax_error,
            BadConfigException.Reason.UNKNOWN_ATTRIBUTE to R.string.bad_config_reason_unknown_attribute,
            BadConfigException.Reason.UNKNOWN_SECTION to R.string.bad_config_reason_unknown_section
    )
    private val BE_REASON_MAP = mapOf(
            BackendException.Reason.UNKNOWN_KERNEL_MODULE_NAME to R.string.module_version_error,
            BackendException.Reason.WG_QUICK_CONFIG_ERROR_CODE to R.string.tunnel_config_error,
            BackendException.Reason.TUNNEL_MISSING_CONFIG to R.string.no_config_error,
            BackendException.Reason.VPN_NOT_AUTHORIZED to R.string.vpn_not_authorized_error,
            BackendException.Reason.UNABLE_TO_START_VPN to R.string.vpn_start_error,
            BackendException.Reason.TUN_CREATION_ERROR to R.string.tun_create_error,
            BackendException.Reason.GO_ACTIVATION_ERROR_CODE to R.string.tunnel_on_error
    )
    private val KFE_FORMAT_MAP = mapOf(
            Key.Format.BASE64 to R.string.key_length_explanation_base64,
            Key.Format.BINARY to R.string.key_length_explanation_binary,
            Key.Format.HEX to R.string.key_length_explanation_hex
    )
    private val KFE_TYPE_MAP = mapOf(
            KeyFormatException.Type.CONTENTS to R.string.key_contents_error,
            KeyFormatException.Type.LENGTH to R.string.key_length_error
    )
    private val PE_CLASS_MAP = mapOf(
            InetAddress::class.java to R.string.parse_error_inet_address,
            InetEndpoint::class.java to R.string.parse_error_inet_endpoint,
            InetNetwork::class.java to R.string.parse_error_inet_network,
            Int::class.java to R.string.parse_error_integer
    )
    private val RSE_REASON_MAP = mapOf(
            RootShellException.Reason.NO_ROOT_ACCESS to R.string.error_root,
            RootShellException.Reason.SHELL_MARKER_COUNT_ERROR to R.string.shell_marker_count_error,
            RootShellException.Reason.SHELL_EXIT_STATUS_READ_ERROR to R.string.shell_exit_status_read_error,
            RootShellException.Reason.SHELL_START_ERROR to R.string.shell_start_error,
            RootShellException.Reason.CREATE_BIN_DIR_ERROR to R.string.create_bin_dir_error,
            RootShellException.Reason.CREATE_TEMP_DIR_ERROR to R.string.create_temp_dir_error
    )

    operator fun get(throwable: Throwable?): String {
        val resources = get().resources
        if (throwable == null) return resources.getString(R.string.unknown_error)
        val rootCause = rootCause(throwable)
        return when {
            rootCause is BadConfigException -> {
                val reason = getBadConfigExceptionReason(resources, rootCause)
                val context = if (rootCause.location == BadConfigException.Location.TOP_LEVEL) {
                    resources.getString(R.string.bad_config_context_top_level, rootCause.section.getName())
                } else {
                    resources.getString(R.string.bad_config_context, rootCause.section.getName(), rootCause.location.getName())
                }
                val explanation = getBadConfigExceptionExplanation(resources, rootCause)
                resources.getString(R.string.bad_config_error, reason, context) + explanation
            }
            rootCause is BackendException -> {
                resources.getString(BE_REASON_MAP.getValue(rootCause.reason), *rootCause.format)
            }
            rootCause is RootShellException -> {
                resources.getString(RSE_REASON_MAP.getValue(rootCause.reason), *rootCause.format)
            }
            rootCause.message != null -> {
                rootCause.message!!
            }
            else -> {
                val errorType = rootCause.javaClass.simpleName
                resources.getString(R.string.generic_error, errorType)
            }
        }
    }

    private fun getBadConfigExceptionExplanation(resources: Resources,
                                                 bce: BadConfigException): String {
        if (bce.cause is KeyFormatException) {
            val kfe = bce.cause as KeyFormatException?
            if (kfe!!.type == KeyFormatException.Type.LENGTH) return resources.getString(KFE_FORMAT_MAP.getValue(kfe.format))
        } else if (bce.cause is ParseException) {
            val pe = bce.cause as ParseException?
            if (pe!!.message != null) return ": ${pe.message}"
        } else if (bce.location == BadConfigException.Location.LISTEN_PORT) {
            return resources.getString(R.string.bad_config_explanation_udp_port)
        } else if (bce.location == BadConfigException.Location.MTU) {
            return resources.getString(R.string.bad_config_explanation_positive_number)
        } else if (bce.location == BadConfigException.Location.PERSISTENT_KEEPALIVE) {
            return resources.getString(R.string.bad_config_explanation_pka)
        }
        return ""
    }

    private fun getBadConfigExceptionReason(resources: Resources,
                                            bce: BadConfigException): String {
        if (bce.cause is KeyFormatException) {
            val kfe = bce.cause as KeyFormatException?
            return resources.getString(KFE_TYPE_MAP.getValue(kfe!!.type))
        } else if (bce.cause is ParseException) {
            val pe = bce.cause as ParseException?
            val type = resources.getString((if (PE_CLASS_MAP.containsKey(pe!!.parsingClass)) PE_CLASS_MAP[pe.parsingClass] else R.string.parse_error_generic)!!)
            return resources.getString(R.string.parse_error_reason, type, pe.text)
        }
        return resources.getString(BCE_REASON_MAP.getValue(bce.reason), bce.text)
    }

    private fun rootCause(throwable: Throwable): Throwable {
        var cause = throwable
        while (cause.cause != null) {
            if (cause is BadConfigException || cause is BackendException ||
                    cause is RootShellException) break
            val nextCause = cause.cause!!
            if (nextCause is RemoteException) break
            cause = nextCause
        }
        return cause
    }
}