@@ -46,9 +46,14 @@ static ClusterRuntime get() {
4646 private volatile boolean running ;
4747 private volatile Thread worker ;
4848 private volatile String lastStateDigest = "" ;
49+ private volatile String selfDexotePub = "" ;
4950
5051 private ClusterRuntime () {}
5152
53+ void setSelfDexotePub (String pubBase64 ) {
54+ selfDexotePub = pubBase64 == null ? "" : pubBase64 .trim ();
55+ }
56+
5257 void start (Config cfg ) {
5358 this .cfg = cfg ;
5459 registerSelf ();
@@ -75,7 +80,7 @@ void registerSelf() {
7580 endpoint = "http://" + host .trim () + ":" + c .listenPorts ().get (0 );
7681 dhtRpc = selfDhtRpc (host , c .dhtRpcListenUdp ());
7782 }
78- nodes .put (c .clusterNodeId (), new ClusterNode (c .clusterNodeId (), endpoint , dhtRpc , System .currentTimeMillis (), true ));
83+ nodes .put (c .clusterNodeId (), new ClusterNode (c .clusterNodeId (), endpoint , dhtRpc , selfDexotePub , System .currentTimeMillis (), true ));
7984 }
8085
8186 private String resolveSelfHost (Config c ) {
@@ -169,6 +174,57 @@ boolean isOwnAddress(InetSocketAddress addr) {
169174 }
170175
171176 Optional <InetSocketAddress > resolveClusterExitDialAddress (String hint ) {
177+ return resolveClusterExit (hint ).map (t -> t .addr );
178+ }
179+
180+ ф static final class ClusterExitTarget {
181+ final InetSocketAddress addr ;
182+ final byte [] dexotePub ;
183+
184+ ClusterExitTarget (InetSocketAddress addr , byte [] dexotePub ) {
185+ this .addr = addr ;
186+ this .dexotePub = dexotePub ;
187+ }
188+ }
189+
190+ private Optional <byte []> dexotePubForNode (String nodeId ) {
191+ ClusterNode n = nodes .get (nodeId );
192+ if (n == null || n .dexotePub == null || n .dexotePub .isBlank ()) {
193+ return Optional .empty ();
194+ }
195+ try {
196+ byte [] dec = java .util .Base64 .getDecoder ().decode (n .dexotePub .trim ());
197+ if (dec .length != 32 ) {
198+ return Optional .empty ();
199+ }
200+ return Optional .of (dec );
201+ } catch (IllegalArgumentException e ) {
202+ return Optional .empty ();
203+ }
204+ }
205+
206+ private Optional <byte []> dexotePubForAddress (InetSocketAddress want ) {
207+ for (String id : nodes .keySet ()) {
208+ ClusterNode node = nodes .get (id );
209+ if (node == null || !node .alive ) {
210+ continue ;
211+ }
212+ Optional <String > ohp = resolveVolterHttpHostPort (id );
213+ if (ohp .isEmpty ()) {
214+ continue ;
215+ }
216+ try {
217+ InetSocketAddress known = ClusterTcpExitBridge .parseHostPort (ohp .get ());
218+ if (known .getPort () == want .getPort () && known .getAddress ().equals (want .getAddress ())) {
219+ return dexotePubForNode (id );
220+ }
221+ } catch (Exception ignored ) {
222+ }
223+ }
224+ return Optional .empty ();
225+ }
226+
227+ Optional <ClusterExitTarget > resolveClusterExit (String hint ) {
172228 if (hint == null || hint .isBlank ()) {
173229 return Optional .empty ();
174230 }
@@ -186,7 +242,12 @@ Optional<InetSocketAddress> resolveClusterExitDialAddress(String hint) {
186242 log .info ("cluster exit " + h + " resolved to self: " + addr + ", skip bridge" );
187243 return Optional .empty ();
188244 }
189- return Optional .of (addr );
245+ Optional <byte []> pub = dexotePubForNode (h );
246+ if (pub .isEmpty ()) {
247+ log .warning ("cluster exit " + h + " has no dexotePub in cluster map, skip bridge" );
248+ return Optional .empty ();
249+ }
250+ return Optional .of (new ClusterExitTarget (addr , pub .get ()));
190251 } catch (Exception e ) {
191252 return Optional .empty ();
192253 }
@@ -199,7 +260,12 @@ Optional<InetSocketAddress> resolveClusterExitDialAddress(String hint) {
199260 return Optional .empty ();
200261 }
201262 if (isAuthorizedClusterExit (h )) {
202- return Optional .of (addr );
263+ Optional <byte []> pub = dexotePubForAddress (addr );
264+ if (pub .isEmpty ()) {
265+ log .warning ("cluster exit " + h + " has no dexotePub in cluster map, skip bridge" );
266+ return Optional .empty ();
267+ }
268+ return Optional .of (new ClusterExitTarget (addr , pub .get ()));
203269 }
204270 } catch (Exception ignored ) {
205271 }
@@ -252,6 +318,9 @@ String clusterMapJson() {
252318 if (n .dhtRpc != null && !n .dhtRpc .isBlank ()) {
253319 sb .append ("\" dhtRpc\" :\" " ).append (json (n .dhtRpc )).append ("\" ," );
254320 }
321+ if (n .dexotePub != null && !n .dexotePub .isBlank ()) {
322+ sb .append ("\" dexotePub\" :\" " ).append (json (n .dexotePub )).append ("\" ," );
323+ }
255324 sb .append ("\" ts\" :" ).append (n .lastSeenMs ).append ("," );
256325 sb .append ("\" alive\" :" ).append (n .alive ).append ("}" );
257326 }
@@ -390,7 +459,7 @@ private void markPeerDown(String endpoint) {
390459 for (Map .Entry <String , ClusterNode > e : nodes .entrySet ()) {
391460 if (!normalizeEndpoint (e .getValue ().endpoint ).equals (want )) continue ;
392461 ClusterNode n = e .getValue ();
393- nodes .put (e .getKey (), new ClusterNode (n .nodeId , n .endpoint , n .dhtRpc , System .currentTimeMillis (), false ));
462+ nodes .put (e .getKey (), new ClusterNode (n .nodeId , n .endpoint , n .dhtRpc , n . dexotePub , System .currentTimeMillis (), false ));
394463 }
395464 }
396465
@@ -470,6 +539,7 @@ private int mergeFromJson(String raw) {
470539 String id = row .getOrDefault ("id" , "" ).trim ();
471540 String endpoint = row .getOrDefault ("endpoint" , "" ).trim ();
472541 String dhtRpc = row .getOrDefault ("dhtRpc" , "" ).trim ();
542+ String dexotePub = row .getOrDefault ("dexotePub" , "" ).trim ();
473543 if (id .isEmpty ()) continue ;
474544 if (endpoint .isEmpty ()) endpoint = "" ;
475545 long seen = now ;
@@ -480,7 +550,7 @@ private int mergeFromJson(String raw) {
480550 } catch (NumberFormatException ignored ) {
481551 }
482552 }
483- nodes .put (id , new ClusterNode (id , endpoint , dhtRpc , seen , true ));
553+ nodes .put (id , new ClusterNode (id , endpoint , dhtRpc , dexotePub , seen , true ));
484554 merged ++;
485555 }
486556 return merged ;
@@ -516,10 +586,12 @@ private static List<Map<String, String>> parseNodes(String json) {
516586 String id = jsonField (c , "id" );
517587 String endpoint = jsonField (c , "endpoint" );
518588 String dhtRpc = jsonField (c , "dhtRpc" );
589+ String dexotePub = jsonField (c , "dexotePub" );
519590 String ts = jsonFieldLong (c , "ts" );
520591 if (id != null ) m .put ("id" , id );
521592 if (endpoint != null ) m .put ("endpoint" , endpoint );
522593 if (dhtRpc != null ) m .put ("dhtRpc" , dhtRpc );
594+ if (dexotePub != null ) m .put ("dexotePub" , dexotePub );
523595 if (ts != null ) m .put ("ts" , ts );
524596 if (!m .isEmpty ()) out .add (m );
525597 }
@@ -607,13 +679,15 @@ private static final class ClusterNode {
607679 final String nodeId ;
608680 final String endpoint ;
609681 final String dhtRpc ;
682+ final String dexotePub ;
610683 final long lastSeenMs ;
611684 final boolean alive ;
612685
613- ClusterNode (String nodeId , String endpoint , String dhtRpc , long lastSeenMs , boolean alive ) {
686+ ClusterNode (String nodeId , String endpoint , String dhtRpc , String dexotePub , long lastSeenMs , boolean alive ) {
614687 this .nodeId = nodeId ;
615688 this .endpoint = endpoint ;
616689 this .dhtRpc = dhtRpc ;
690+ this .dexotePub = dexotePub == null ? "" : dexotePub ;
617691 this .lastSeenMs = lastSeenMs ;
618692 this .alive = alive ;
619693 }
0 commit comments