diff --git a/tests/test_android_replies.c b/tests/test_android_replies.c index dc490e7f..7bbd0e11 100644 --- a/tests/test_android_replies.c +++ b/tests/test_android_replies.c @@ -75,7 +75,19 @@ static int check_addresses(const char *channel) { REQUIRE(!extract(json, "$.adr[1].pt", value, sizeof(value)) && !strcmp(value, "TCP")); REQUIRE(!extract(json, "$.adr[1].po", value, sizeof(value)) && !strcmp(value, "443")); REQUIRE(!extract(json, "$.adr[1].si", value, sizeof(value)) && !strcmp(value, "6")); + REQUIRE(!extract(json, "$.adr[0].nmapping", value, sizeof(value)) && !strcmp(value, "0")); + REQUIRE(!extract(json, "$.adr[0].nsource", value, sizeof(value)) && !strcmp(value, "0")); u_free(json); + /* addr_type=0 остаётся тем же, но измеренный UNCHANGED должен дойти до GUI отдельно. */ + meta4.address_profile = (struct address_profile){ADDRESS_MAP_UNCHANGED, ADDRESS_IN_UNKNOWN, ADDRESS_SOURCE_ETCP}; + REQUIRE(!topo_node_sqlite_addrs_put(instance->topo_sqlite_db, instance->node_id, &node)); + json = utun_bridge_get_member_detail_json(channel, instance->node_id); + REQUIRE(json && !extract(json, "$.adr[0].at", value, sizeof(value)) && !strcmp(value, "0")); + REQUIRE(!extract(json, "$.adr[0].nmapping", value, sizeof(value)) && !strcmp(value, "1")); + REQUIRE(!extract(json, "$.adr[0].ninbound", value, sizeof(value)) && !strcmp(value, "0")); + REQUIRE(!extract(json, "$.adr[0].nsource", value, sizeof(value)) && !strcmp(value, "1")); + u_free(json); + meta4.address_profile.mapping = ADDRESS_MAP_UNKNOWN; addr4.type = TOPO_ADDR_NAT; meta4.address_profile.source = ADDRESS_SOURCE_ETCP; REQUIRE(!topo_node_sqlite_addrs_put(instance->topo_sqlite_db, instance->node_id, &node)); json = utun_bridge_get_member_detail_json(channel, instance->node_id); diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt index d585cc9d..d0c9116b 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt @@ -99,13 +99,22 @@ data class MemberDetail(val nodeId: Long, val name: String = "", val inviterId: Long? = null, val inviterName: String = "") data class MemberAddr(val family: Int = 0, val protocol: String = "", val address: String = "", - val port: Int = 0, val socketId: Int = 0, val addrType: Int = 0, val rtt: Int = 0) { - val roleLabelRes: Int get() = when (addrType) { - 0 -> R.string.address_local; 1 -> R.string.address_public - 2 -> R.string.address_nat_eim; 3 -> R.string.address_nat_dependent - 4 -> R.string.address_nat_unknown; 6 -> R.string.address_nat_eim_unverified - 5 -> R.string.address_contact; else -> R.string.address_unknown - } + val port: Int = 0, val socketId: Int = 0, val addrType: Int = 0, val rtt: Int = 0, + val natMapping: Int = 0, val natInbound: Int = 0, val natSource: Int = 0) { + val natLabelRes: Int get() = natProfileLabelRes(natMapping, natInbound, natSource) +} + +// Происхождение адреса не определяет NAT: результат берём из измеренного профиля сокета. +private fun natProfileLabelRes(mapping: Int, inbound: Int, source: Int): Int = when { + source == 0 -> R.string.socket_nat_unmeasured + mapping == 1 && inbound == 1 -> R.string.socket_no_nat_open + mapping == 1 && inbound == 2 -> R.string.socket_no_nat_filtered + mapping == 1 -> R.string.socket_no_nat + mapping == 2 && inbound == 1 -> R.string.socket_eim_open + mapping == 2 && inbound == 2 -> R.string.socket_eim_filtered + mapping == 2 -> R.string.address_nat_eim_unverified + mapping == 3 -> R.string.address_nat_dependent + else -> R.string.address_nat_unknown } data class LinkStatus(val linkId: Int = 0, val localFamily: Int = 0, val localAddr: String = "", val localPort: Int = 0, @@ -127,15 +136,7 @@ data class LocalSocketState( val natMapping: Int = 0, val natInbound: Int = 0, val natSource: Int = 0, val externalIp: String = "", val externalPort: Int = 0 ) { - val natLabelRes: Int get() = when { - natSource == 0 -> R.string.socket_nat_unmeasured - natMapping == 1 -> R.string.socket_no_nat - natMapping == 2 && natInbound == 1 -> R.string.socket_eim_open - natMapping == 2 && natInbound == 2 -> R.string.socket_eim_filtered - natMapping == 2 -> R.string.address_nat_eim_unverified - natMapping == 3 -> R.string.address_nat_dependent - else -> R.string.address_nat_unknown - } + val natLabelRes: Int get() = natProfileLabelRes(natMapping, natInbound, natSource) val stateLabel: String get() = AppLanguage.text(when (state) { 2 -> R.string.connection_up; 1 -> R.string.connection_connecting; else -> R.string.connection_down }) @@ -488,7 +489,10 @@ class ChatRepository { port = a.optInt("po", 0), socketId = a.optInt("si", 0), addrType = a.optInt("at", 0), - rtt = a.optInt("rt", 0) + rtt = a.optInt("rt", 0), + natMapping = a.getInt("nmapping"), + natInbound = a.getInt("ninbound"), + natSource = a.getInt("nsource") )) } } diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 5c233965..2783a0cf 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c @@ -885,7 +885,7 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i if (member_detail_append(buf, sizeof(buf), &pos, ",\"adr\":[") < 0) return u_strdup("{}"); if (sqlite3_prepare_v2(db, - "SELECT family, protocol, address, port, socket_id, addr_type, COALESCE(rtt,0)" + "SELECT family, protocol, address, port, socket_id, addr_type, COALESCE(rtt,0), nat_mapping, nat_inbound, nat_source" " FROM node_addresses WHERE node_id=? ORDER BY socket_id, addr_type, family, port", -1, &st, NULL) == SQLITE_OK) { sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); @@ -914,8 +914,10 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i if (proto & 0x04) pn = "TCP+REALITY"; else switch (proto) { case 1: pn = "UDP"; break; case 2: pn = "TCP"; break; default: pn = "UDP/TCP"; break; } if (member_detail_append(buf, sizeof(buf), &pos, - "%s{\"fm\":%d,\"pt\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"si\":%d,\"at\":%d,\"rt\":%d}", - af ? "" : ",", family, pn, addr_str[0] ? addr_str : "?", port, sid, at, rtt) < 0) { + "%s{\"fm\":%d,\"pt\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"si\":%d,\"at\":%d,\"rt\":%d," + "\"nmapping\":%d,\"ninbound\":%d,\"nsource\":%d}", + af ? "" : ",", family, pn, addr_str[0] ? addr_str : "?", port, sid, at, rtt, + sqlite3_column_int(st, 7), sqlite3_column_int(st, 8), sqlite3_column_int(st, 9)) < 0) { sqlite3_finalize(st); return u_strdup("{}"); } af = 0;