chat: invite-ссылки с несколькими адресами + ncd-адреса узлов (join)
This commit is contained in:
1 parent
d88dccfa19
commit
481872d5db
24 files changed
+517
-114
No files matched your search
@@ -4,19 +4,28 @@ import android.util.Base64
|
||||
import com.utun.chat.data.LogManager
|
||||
import java.security.MessageDigest
|
||||
|
||||
const val INVITE_LINK_VERSION: Int = 0x01
|
||||
const val INVITE_LINK_VERSION: Int = 0x03
|
||||
const val INVITE_PUBKEY_SIZE: Int = 32
|
||||
const val INVITE_SNAME_MAX: Int = 64
|
||||
|
||||
const val INVITE_PROTO_UDP: Int = 0x01
|
||||
const val INVITE_PROTO_TCP: Int = 0x02
|
||||
const val INVITE_PROTO_BOTH: Int = 0x03
|
||||
const val INVITE_PROTO_REALITY: Int = 0x04 // флаг reality-камуфляжа поверх TCP (reality-TCP = 0x06)
|
||||
|
||||
data class InviteReality(
|
||||
val has: Boolean,
|
||||
val shortId: ByteArray,
|
||||
val serverPubkey: ByteArray,
|
||||
val version: ByteArray,
|
||||
val serverName: String
|
||||
)
|
||||
|
||||
data class InviteAddr(
|
||||
val family: Int, // 4 or 6
|
||||
val address: ByteArray,
|
||||
val port: Int,
|
||||
val socketId: Int,
|
||||
val proto: Int // INVITE_PROTO_UDP/TCP/BOTH
|
||||
val proto: Int // INVITE_PROTO_*
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
@@ -32,13 +41,26 @@ data class InviteData(
|
||||
val joinKey: Long,
|
||||
val pubkey: ByteArray,
|
||||
val nodeId: Long,
|
||||
val addrs: List<InviteAddr>
|
||||
val addrs: List<InviteAddr>,
|
||||
val password: String = "",
|
||||
val reality: InviteReality? = null
|
||||
) {
|
||||
/** Serialize addresses for C bridge: family(1) + socketId(1) + proto(1) + address(4|16) + port(2 BE) */
|
||||
/** Serialize addresses for C bridge: reality-префикс + family(1)+socketId(1)+proto(1)+address(4|16)+port(2 BE) */
|
||||
fun serializeAddrs(): ByteArray {
|
||||
val size = addrs.fold(0) { acc, a -> acc + if (a.family == 4) 9 else 21 }
|
||||
var size = 1
|
||||
if (reality != null && reality.has) size += 8 + 32 + 3 + 1 + reality.serverName.toByteArray(Charsets.UTF_8).let { minOf(it.size, INVITE_SNAME_MAX - 1) }
|
||||
size = addrs.fold(size) { acc, a -> acc + if (a.family == 4) 9 else 21 }
|
||||
val buf = ByteArray(size)
|
||||
var pos = 0
|
||||
buf[pos] = (if (reality != null && reality.has) 1 else 0).toByte(); pos++
|
||||
if (reality != null && reality.has) {
|
||||
System.arraycopy(reality.shortId, 0, buf, pos, 8); pos += 8
|
||||
System.arraycopy(reality.serverPubkey, 0, buf, pos, 32); pos += 32
|
||||
System.arraycopy(reality.version, 0, buf, pos, 3); pos += 3
|
||||
val sn = reality.serverName.toByteArray(Charsets.UTF_8).let { if (it.size > INVITE_SNAME_MAX - 1) it.copyOf(INVITE_SNAME_MAX - 1) else it }
|
||||
buf[pos] = sn.size.toByte(); pos++
|
||||
System.arraycopy(sn, 0, buf, pos, sn.size); pos += sn.size
|
||||
}
|
||||
for (a in addrs) {
|
||||
buf[pos] = a.family.toByte(); pos++
|
||||
buf[pos] = a.socketId.toByte(); pos++
|
||||
@@ -85,13 +107,25 @@ object InviteLink {
|
||||
}
|
||||
|
||||
var off = 0
|
||||
if ((raw[off].toInt() and 0xFF) != INVITE_LINK_VERSION) {
|
||||
val ver = raw[off].toInt() and 0xFF
|
||||
if (ver != INVITE_LINK_VERSION && ver != 0x02 && ver != 0x01) {
|
||||
LogManager.addLog("ERROR", "INVITE", "decode: wrong version 0x%02x expected 0x%02x".format(
|
||||
raw[off].toInt() and 0xFF, INVITE_LINK_VERSION))
|
||||
return null
|
||||
}
|
||||
off++
|
||||
|
||||
var password = ""
|
||||
if (ver == 0x02 || ver == INVITE_LINK_VERSION) {
|
||||
if (off >= raw.size) return null
|
||||
val plen = raw[off].toInt() and 0xFF; off++
|
||||
if (plen > 0) {
|
||||
if (off + plen > raw.size) return null
|
||||
password = String(raw.copyOfRange(off, off + plen), Charsets.UTF_8)
|
||||
off += plen
|
||||
}
|
||||
}
|
||||
|
||||
var chId = 0L
|
||||
for (i in 0 until 8) {
|
||||
if (off + i >= raw.size) return null
|
||||
@@ -106,6 +140,24 @@ object InviteLink {
|
||||
}
|
||||
off += 8
|
||||
|
||||
var reality: InviteReality? = null
|
||||
if (ver == INVITE_LINK_VERSION) {
|
||||
if (off >= raw.size) return null
|
||||
val has = (raw[off].toInt() and 0xFF) != 0; off++
|
||||
if (has) {
|
||||
if (off + 8 + 32 + 3 + 1 > raw.size) return null
|
||||
val shortId = raw.copyOfRange(off, off + 8); off += 8
|
||||
val serverPubkey = raw.copyOfRange(off, off + 32); off += 32
|
||||
val version = raw.copyOfRange(off, off + 3); off += 3
|
||||
var snlen = raw[off].toInt() and 0xFF; off++
|
||||
if (snlen > INVITE_SNAME_MAX - 1) snlen = INVITE_SNAME_MAX - 1
|
||||
if (off + snlen > raw.size) return null
|
||||
val serverName = String(raw.copyOfRange(off, off + snlen), Charsets.UTF_8)
|
||||
off += snlen
|
||||
reality = InviteReality(has = true, shortId = shortId, serverPubkey = serverPubkey, version = version, serverName = serverName)
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.addLog("DEBUG", "INVITE", "decode: chId=0x%016x joinKey=0x%016x".format(chId, joinKey))
|
||||
|
||||
var pubkey: ByteArray? = null
|
||||
@@ -143,14 +195,15 @@ object InviteLink {
|
||||
val pkHex = pubkey.take(8).joinToString("") { "%02x".format(it) }
|
||||
LogManager.addLog("INFO", "INVITE", "decode: ch=0x%016x node=0x%016x pk=%s... addrs=%d".format(
|
||||
chId, nodeId, pkHex, addrs.size))
|
||||
return InviteData(channelId = chId, joinKey = joinKey, pubkey = pubkey, nodeId = nodeId, addrs = addrs)
|
||||
return InviteData(channelId = chId, joinKey = joinKey, pubkey = pubkey, nodeId = nodeId,
|
||||
addrs = addrs, password = password, reality = reality)
|
||||
}
|
||||
|
||||
fun addrToString(addr: InviteAddr): String {
|
||||
val protoStr = when (addr.proto) {
|
||||
INVITE_PROTO_UDP -> "UDP"
|
||||
INVITE_PROTO_TCP -> "TCP"
|
||||
INVITE_PROTO_BOTH -> "UDP+TCP"
|
||||
val protoStr = when {
|
||||
(addr.proto and INVITE_PROTO_REALITY) != 0 -> "TCP+REALITY"
|
||||
addr.proto == INVITE_PROTO_UDP -> "UDP"
|
||||
addr.proto == INVITE_PROTO_TCP -> "TCP"
|
||||
else -> "?${addr.proto}"
|
||||
}
|
||||
val ip = if (addr.family == 4 && addr.address.size == 4) {
|
||||
|
||||
Reference in new issue
Block a user