Show detailed error for failed connections in VPN mode

Also change such errors colors from red to orange, as they
are normal during a capture.

Closes #441
This commit is contained in:
emanuele-f
2025-02-20 10:02:44 +01:00
parent 18adc58124
commit 529ef639b6
9 changed files with 123 additions and 4 deletions
@@ -147,6 +147,10 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
else if((conn.status == ConnectionDescriptor.CONN_STATUS_CLOSED)
|| (conn.status == ConnectionDescriptor.CONN_STATUS_RESET))
color = R.color.statusClosed;
else if ((conn.status == ConnectionDescriptor.CONN_STATUS_ERROR)
|| ((conn.status == ConnectionDescriptor.CONN_STATUS_SOCKET_ERROR))
|| (conn.status == ConnectionDescriptor.CONN_STATUS_UNREACHABLE))
color = R.color.warning;
else
color = R.color.statusError;
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -70,6 +71,9 @@ public class ConnectionOverview extends Fragment implements ConnectionDetailsAct
private TextView mLastSeen;
//private TextView mTcpFlags;
private TextView mError;
private TextView mSocketErrno;
private View mSocketErrnoRow;
private View mSocketErrnoInfo;
private ImageView mBlacklistedIp;
private ImageView mBlacklistedHost;
@@ -130,6 +134,9 @@ public class ConnectionOverview extends Fragment implements ConnectionDetailsAct
mLastSeen = view.findViewById(R.id.last_seen);
//mTcpFlags = view.findViewById(R.id.tcp_flags);
mError = view.findViewById(R.id.error_msg);
mSocketErrno = view.findViewById(R.id.detail_errno);
mSocketErrnoRow = view.findViewById(R.id.error_row);
mSocketErrnoInfo = view.findViewById(R.id.error_info);
mBlacklistedIp = view.findViewById(R.id.blacklisted_ip);
mBlacklistedHost = view.findViewById(R.id.blacklisted_host);
@@ -280,6 +287,26 @@ public class ConnectionOverview extends Fragment implements ConnectionDetailsAct
mBlacklistedIp.setVisibility(mConn.isBlacklistedIp() ? View.VISIBLE : View.GONE);
mBlacklistedHost.setVisibility(mConn.isBlacklistedHost() ? View.VISIBLE : View.GONE);
if (mConn.error > 0) {
mSocketErrnoRow.setVisibility(View.VISIBLE);
Pair<Integer, Integer> errnoInfo = getSocketErrnoInfo(mConn.error);
mSocketErrno.setText(context.getString(R.string.error_code_with_text,
context.getString((errnoInfo != null) ? errnoInfo.first : R.string.unknown_app),
mConn.error));
if (errnoInfo != null) {
final int msgId = errnoInfo.second;
mSocketErrnoInfo.setOnClickListener(view -> {
Context ctx = getContext();
if (ctx != null)
Utils.showHelpDialog(ctx, msgId);
});
} else
mSocketErrnoInfo.setVisibility(View.GONE);
}
if(mConn.decryption_error != null) {
mError.setTextColor(ContextCompat.getColor(context, R.color.danger));
mError.setText(mConn.decryption_error);
@@ -316,4 +343,28 @@ public class ConnectionOverview extends Fragment implements ConnectionDetailsAct
} else
mError.setVisibility(View.GONE);
}
private Pair<Integer, Integer> getSocketErrnoInfo(int errno) {
return switch (errno) {
case 32 -> /* EPIPE */
new Pair<>(R.string.errno_epipe, R.string.errno_epipe_msg);
case 100 -> /* ENETDOWN */
new Pair<>(R.string.errno_enetdown, R.string.errno_enetdown_msg);
case 101 -> /* ENETUNREACH */
new Pair<>(R.string.errno_enetunreach, R.string.errno_enetunreach_msg);
case 102 -> /* ENETRESET */
new Pair<>(R.string.errno_enetreset, R.string.errno_enetreset_msg);
case 103 -> /* ECONNABORTED */
new Pair<>(R.string.errno_econnaborted, R.string.errno_econnaborted_msg);
case 104 -> /* ECONNRESET */
new Pair<>(R.string.errno_econnreset, R.string.errno_econnreset_msg);
case 110 -> /* ETIMEDOUT */
new Pair<>(R.string.errno_etimedout, R.string.errno_etimedout_msg);
case 111 -> /* ECONNREFUSED */
new Pair<>(R.string.errno_econnrefused, R.string.errno_econnrefused_msg);
case 113 -> /* EHOSTUNREACH */
new Pair<>(R.string.errno_ehostunreach, R.string.errno_ehostunreach_msg);
default -> null;
};
}
}
@@ -109,6 +109,7 @@ public class ConnectionDescriptor {
private final boolean mitm_decrypt; // true if the connection is under mitm for TLS decryption
private boolean internal_decrypt;
public int status;
public int error;
private int tcp_flags;
private boolean blacklisted_ip;
private boolean blacklisted_host;
@@ -160,6 +161,7 @@ public class ConnectionDescriptor {
rcvd_pkts = update.rcvd_pkts;
blocked_pkts = update.blocked_pkts;
status = (update.status & 0x00FF);
error = (update.status & 0xFF0000) >> 16;
port_mapping_applied = (update.status & 0x2000) != 0;
decryption_ignored = (update.status & 0x1000) != 0;
netd_block_missed = (update.status & 0x0800) != 0;
+4 -1
View File
@@ -334,6 +334,7 @@ static void connection_closed(zdtun_t *zdt, const zdtun_conn_t *conn_info) {
pd_giveup_dpi(pd, data, tuple);
data->status = zdtun_conn_get_status(conn_info);
data->error = zdtun_conn_get_error(conn_info);
data->to_purge = true;
}
@@ -346,8 +347,10 @@ static void update_conn_status(zdtun_t *zdt, const zdtun_pkt_t *pkt, uint8_t fro
// Update the connection status
data->status = zdtun_conn_get_status(conn_info);
if(data->status >= CONN_STATUS_CLOSED)
if(data->status >= CONN_STATUS_CLOSED) {
data->to_purge = true;
data->error = zdtun_conn_get_error(conn_info);
}
}
/* ******************************************************* */
+2 -1
View File
@@ -169,7 +169,8 @@ static jobject getConnUpdate(pcapdroid_t *pd, const conn_and_tuple_t *conn) {
(*env)->CallVoidMethod(env, update, mids.connUpdateSetStats, data->last_seen,
data->payload_length, data->sent_bytes, data->rcvd_bytes, data->sent_pkts, data->rcvd_pkts, data->blocked_pkts,
(data->tcp_flags[0] << 8) | data->tcp_flags[1],
(data->port_mapping_applied << 13) |
(data->error << 16) /* 8 bits are enough for socket errno */ |
(data->port_mapping_applied << 13) |
(data->decryption_ignored << 12) |
(data->netd_block_missed << 11) |
(blocked << 10) |
+1
View File
@@ -105,6 +105,7 @@ typedef struct {
jint rcvd_pkts;
jint blocked_pkts;
zdtun_conn_status_t status;
int error;
char *info;
jint uid;
char country_code[3];
@@ -177,6 +177,43 @@
tools:text="Open" />
</TableRow>
<TableRow
android:id="@+id/error_row"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="4dp"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.25"
android:textStyle="bold"
android:text="@string/error" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.75"
android:orientation="horizontal">
<TextView
android:id="@+id/detail_errno"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textColor="@color/warning"
tools:text="No route to host (error 113)" />
<ImageView
android:id="@+id/error_info"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingTop="1sp"
android:src="@drawable/ic_info" />
</LinearLayout>
</TableRow>
<TableRow
android:id="@+id/interface_row"
android:layout_width="match_parent"
@@ -496,6 +533,7 @@
android:textIsSelectable="true"
android:layout_marginTop="8dp"
android:visibility="gone"
tools:visibility="visible"
tools:text="The client may not trust certificate" />
</androidx.constraintlayout.widget.ConstraintLayout>
+20 -1
View File
@@ -507,7 +507,7 @@
<string name="pcap_file_load_aborted">Capture file loading aborted</string>
<string name="host_resolution_failed">"Could not resolve host %1$s</string>
<string name="pcapdroid_trailer_notice">To show the actual apps instead of \"%1$s\", be sure to enable the \"%2$s\" option before generating the dump</string>
<string name="decryption_info_no_rule">This connection will not be decrypted. Create a decryption rule to decrypt it</string>
<string name="decryption_info_no_rule">This connection will not be decrypted. Create a decryption rule to decrypt it (e.g from the left drawer)</string>
<string name="tls_decryption_no_rules_notice">TLS decryption is only applied to connections that match the configured rules. Do you want to create decryption rules now?</string>
<string name="active_vpn_detected">Active VPN detected</string>
<string name="mitm_doze_notice">Battery optimization may interfere with the mitm addon</string>
@@ -533,4 +533,23 @@
<string name="larger_than">Larger than</string>
<string name="select_the_pcap_file">Select the PCAP/Pcapng file</string>
<string name="select_the_keylog_file">Select the SSL keylog file</string>
<string name="error_code_with_text">%1$s (error %2$d)</string>
<string name="errno_epipe">Broken pipe</string>
<string name="errno_epipe_msg">The server unexpectedly closed the connection during the data transfer</string>
<string name="errno_enetdown">Network is down</string>
<string name="errno_enetdown_msg">The network interface is down or it\'s unable to connect to the network</string>
<string name="errno_enetunreach">Network is unreachable</string>
<string name="errno_enetunreach_msg">The system cannot find a valid route to the destination network</string>
<string name="errno_enetreset">Network reset</string>
<string name="errno_enetreset_msg">The network connection was reset by an intermediary device (e.g. router or mobile carrier)</string>
<string name="errno_econnaborted">Connection aborted</string>
<string name="errno_econnaborted_msg">The connection was aborted, usually due to a network transition</string>
<string name="errno_econnreset">Connection reset by peer</string>
<string name="errno_econnreset_msg">The server abruptly closed the connection, usually due to a crash or ungraceful shutdown</string>
<string name="errno_etimedout">Connection timed out</string>
<string name="errno_etimedout_msg">The server took too long to respond, likely due to network delays or a busy server</string>
<string name="errno_econnrefused">Connection refused</string>
<string name="errno_econnrefused_msg">The server rejected the connection, which may indicate a too busy or malfunctioning server</string>
<string name="errno_ehostunreach">No route to host</string>
<string name="errno_ehostunreach_msg">The system cannot find a route to the destination host</string>
</resources>