fix(shell): normalizar IPv4 de peers no Windows
Evita IP IPv6-mapeado com zone id no campo de destino ao abrir conversa pela lista. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
8fe9bee453
commit
e0624403b2
10 files changed
+131
-47
No files matched your search
@@ -1,6 +1,8 @@
|
|||||||
package pitiupi.models;
|
package pitiupi.models;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import pitiupi.presentation.shell.PeerIpv4;
|
||||||
/**
|
/**
|
||||||
* Representa um usuário identificado na rede e as informações necessárias
|
* Representa um usuário identificado na rede e as informações necessárias
|
||||||
* para acompanhar sua presença através de mensagens de heartbeat.
|
* para acompanhar sua presença através de mensagens de heartbeat.
|
||||||
@@ -68,6 +70,6 @@ public class User {
|
|||||||
if (isBroadcastTarget() || address == null) {
|
if (isBroadcastTarget() || address == null) {
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
return userName + " (" + address.getHostAddress() + ")";
|
return userName + " (" + PeerIpv4.fromInetAddress(address) + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,6 @@ package pitiupi.presentation.shell;
|
|||||||
|
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifica mensagens e atividade originadas neste host (eco de rede).
|
* Identifica mensagens e atividade originadas neste host (eco de rede).
|
||||||
*/
|
*/
|
||||||
@@ -28,7 +26,7 @@ public final class LocalUserIdentity {
|
|||||||
|
|
||||||
private static String canonicalIpv4(String ip) {
|
private static String canonicalIpv4(String ip) {
|
||||||
try {
|
try {
|
||||||
return InetAddress.getByName(ip.trim()).getHostAddress();
|
return PeerIpv4.normalize(ip);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return ip.trim();
|
return ip.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package pitiupi.presentation.shell;
|
||||||
|
|
||||||
|
import java.net.Inet4Address;
|
||||||
|
import java.net.Inet6Address;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPv4 legível para peers (UDP/TCP/chat). No Windows, {@link InetAddress#getHostAddress()}
|
||||||
|
* pode devolver forma IPv6 mapeada ({@code 0:0:...:ffff:a9fe:f8c4}) ou sufixo de zona ({@code %7}).
|
||||||
|
*/
|
||||||
|
public final class PeerIpv4 {
|
||||||
|
|
||||||
|
private PeerIpv4() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String fromInetAddress(InetAddress address) {
|
||||||
|
if (address == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (address instanceof Inet4Address) {
|
||||||
|
return address.getHostAddress();
|
||||||
|
}
|
||||||
|
if (address instanceof Inet6Address inet6 && inet6.isIPv4CompatibleAddress()) {
|
||||||
|
byte[] raw = address.getAddress();
|
||||||
|
if (raw.length == 16) {
|
||||||
|
return ipv4FromBytes(raw, 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] raw = address.getAddress();
|
||||||
|
if (raw.length == 16 && isIpv4Mapped(raw)) {
|
||||||
|
return ipv4FromBytes(raw, 12);
|
||||||
|
}
|
||||||
|
return stripZoneId(address.getHostAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String normalize(String text) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String trimmed = stripZoneId(text.trim());
|
||||||
|
try {
|
||||||
|
return fromInetAddress(InetAddress.getByName(trimmed));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isIpv4Mapped(byte[] raw) {
|
||||||
|
return raw[0] == 0 && raw[1] == 0 && raw[2] == 0 && raw[3] == 0
|
||||||
|
&& raw[4] == 0 && raw[5] == 0 && raw[6] == 0 && raw[7] == 0
|
||||||
|
&& raw[8] == 0 && raw[9] == 0 && raw[10] == (byte) 0xff && raw[11] == (byte) 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String ipv4FromBytes(byte[] raw, int offset) {
|
||||||
|
return (raw[offset] & 0xff) + "." + (raw[offset + 1] & 0xff) + "."
|
||||||
|
+ (raw[offset + 2] & 0xff) + "." + (raw[offset + 3] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripZoneId(String host) {
|
||||||
|
if (host == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int zone = host.indexOf('%');
|
||||||
|
return zone >= 0 ? host.substring(0, zone) : host;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,11 +95,14 @@ public final class PluginRoutingPanel extends JPanel {
|
|||||||
|
|
||||||
public void bindPeerSession(String ip, String displayName, String receiveScopeHtml) {
|
public void bindPeerSession(String ip, String displayName, String receiveScopeHtml) {
|
||||||
everyoneInRoom = false;
|
everyoneInRoom = false;
|
||||||
if (ip != null) {
|
String normalized = PeerIpv4.normalize(ip != null ? ip : "");
|
||||||
|
if (!normalized.isEmpty()) {
|
||||||
|
ipField.setText(normalized);
|
||||||
|
} else if (ip != null) {
|
||||||
ipField.setText(ip);
|
ipField.setText(ip);
|
||||||
}
|
}
|
||||||
String who = displayName != null && !displayName.isBlank() ? displayName : ip;
|
String who = displayName != null && !displayName.isBlank() ? displayName : normalized;
|
||||||
sessionBanner.setText(SessionPeerLabels.sessionBannerHtml(who, ip));
|
sessionBanner.setText(SessionPeerLabels.sessionBannerHtml(who, normalized.isEmpty() ? ip : normalized));
|
||||||
setReceiveScopeHint(receiveScopeHtml);
|
setReceiveScopeHint(receiveScopeHtml);
|
||||||
applyRoutingState();
|
applyRoutingState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package pitiupi.presentation.shell;
|
|||||||
import pitiupi.application.presence.UserPeerView;
|
import pitiupi.application.presence.UserPeerView;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nomes amigáveis para abas e banner de sessão com peers.
|
* Nomes amigáveis para abas e banner de sessão com peers.
|
||||||
*/
|
*/
|
||||||
@@ -17,7 +15,7 @@ public final class SessionPeerLabels {
|
|||||||
if (ip == null || ip.isBlank()) {
|
if (ip == null || ip.isBlank()) {
|
||||||
return hintName != null && !hintName.isBlank() ? hintName : "";
|
return hintName != null && !hintName.isBlank() ? hintName : "";
|
||||||
}
|
}
|
||||||
String trimmedIp = ip.trim();
|
String trimmedIp = PeerIpv4.normalize(ip);
|
||||||
if (hintName != null && !hintName.isBlank() && !hintName.trim().equals(trimmedIp)) {
|
if (hintName != null && !hintName.isBlank() && !hintName.trim().equals(trimmedIp)) {
|
||||||
return hintName.trim();
|
return hintName.trim();
|
||||||
}
|
}
|
||||||
@@ -28,7 +26,7 @@ public final class SessionPeerLabels {
|
|||||||
if (view.isSelf() && isLocalIp(trimmedIp)) {
|
if (view.isSelf() && isLocalIp(trimmedIp)) {
|
||||||
return selfLabel(connectionManager);
|
return selfLabel(connectionManager);
|
||||||
}
|
}
|
||||||
if (view.getInetAddress().map(InetAddress::getHostAddress).orElse("").equals(trimmedIp)) {
|
if (view.getInetAddress().map(PeerIpv4::fromInetAddress).orElse("").equals(trimmedIp)) {
|
||||||
String name = view.getDisplayName();
|
String name = view.getDisplayName();
|
||||||
if (name != null && !name.isBlank() && !name.equals(trimmedIp)) {
|
if (name != null && !name.isBlank() && !name.equals(trimmedIp)) {
|
||||||
return name;
|
return name;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package pitiupi.presentation.shell;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class PeerIpv4Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mapsIpv4MappedIpv6ToDottedDecimal() throws Exception {
|
||||||
|
InetAddress mapped = InetAddress.getByName("0:0:0:0:0:ffff:a9fe:f8c4");
|
||||||
|
assertEquals("169.254.248.196", PeerIpv4.fromInetAddress(mapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void stripsZoneIdFromString() {
|
||||||
|
assertEquals("169.254.248.196", PeerIpv4.normalize("0:0:0:0:0:ffff:a9fe:f8c4%7"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void keepsPlainIpv4() throws Exception {
|
||||||
|
InetAddress v4 = InetAddress.getByName("169.254.165.248");
|
||||||
|
assertEquals("169.254.165.248", PeerIpv4.fromInetAddress(v4));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import pitiupi.chat.net.LegacyMpChatMessage;
|
|||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.models.User;
|
import pitiupi.models.User;
|
||||||
import pitiupi.presentation.shell.LocalNetworkInfo;
|
import pitiupi.presentation.shell.LocalNetworkInfo;
|
||||||
|
import pitiupi.presentation.shell.PeerIpv4;
|
||||||
import pitiupi.presentation.shell.LocalUserIdentity;
|
import pitiupi.presentation.shell.LocalUserIdentity;
|
||||||
import pitiupi.presentation.shell.PeerDraftPanel;
|
import pitiupi.presentation.shell.PeerDraftPanel;
|
||||||
import pitiupi.presentation.shell.PluginRoutingPanel;
|
import pitiupi.presentation.shell.PluginRoutingPanel;
|
||||||
@@ -245,14 +246,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String canonicalPeerIp(String ip) {
|
private static String canonicalPeerIp(String ip) {
|
||||||
if (ip == null || ip.isBlank()) {
|
return PeerIpv4.normalize(ip);
|
||||||
return "";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return InetAddress.getByName(ip.trim()).getHostAddress();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return ip.trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectRoomSession() {
|
private void selectRoomSession() {
|
||||||
@@ -275,7 +269,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
peer.getInetAddress().ifPresent(addr ->
|
peer.getInetAddress().ifPresent(addr ->
|
||||||
openPeerTab(addr.getHostAddress(), peer.getDisplayName()));
|
openPeerTab(PeerIpv4.fromInetAddress(addr), peer.getDisplayName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enablePrivateTcpForPeer(UserPeerView peer) {
|
public void enablePrivateTcpForPeer(UserPeerView peer) {
|
||||||
@@ -415,7 +409,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
if (privateChatController != null) {
|
if (privateChatController != null) {
|
||||||
privateChatController.openChat(user);
|
privateChatController.openChat(user);
|
||||||
}
|
}
|
||||||
appendSystemLine(privateKey(user.getAddress().getHostAddress()),
|
appendSystemLine(privateKey(PeerIpv4.fromInetAddress(user.getAddress())),
|
||||||
"Canal TCP privado conectando… envie de novo em instantes.");
|
"Canal TCP privado conectando… envie de novo em instantes.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import pitiupi.control.ConnectionManager;
|
|||||||
import pitiupi.models.User;
|
import pitiupi.models.User;
|
||||||
import pitiupi.net.tcp.TcpConnection;
|
import pitiupi.net.tcp.TcpConnection;
|
||||||
import pitiupi.presentation.shell.LocalNetworkInfo;
|
import pitiupi.presentation.shell.LocalNetworkInfo;
|
||||||
|
import pitiupi.presentation.shell.PeerIpv4;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -29,14 +30,14 @@ public class PrivateChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String addressKey(TcpConnection connection) {
|
private static String addressKey(TcpConnection connection) {
|
||||||
return connection.getAddress().getHostAddress();
|
return PeerIpv4.fromInetAddress(connection.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openChat(User user) {
|
public void openChat(User user) {
|
||||||
if (user == null || user.getAddress() == null) {
|
if (user == null || user.getAddress() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String address = user.getAddress().getHostAddress();
|
String address = PeerIpv4.fromInetAddress(user.getAddress());
|
||||||
if (connections.containsKey(address)) {
|
if (connections.containsKey(address)) {
|
||||||
if (uiSink != null) {
|
if (uiSink != null) {
|
||||||
uiSink.onPrivateChannelReady(address);
|
uiSink.onPrivateChannelReady(address);
|
||||||
@@ -77,7 +78,7 @@ public class PrivateChatController {
|
|||||||
if (user == null || user.getAddress() == null || text == null || text.isBlank()) {
|
if (user == null || user.getAddress() == null || text == null || text.isBlank()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String address = user.getAddress().getHostAddress();
|
String address = PeerIpv4.fromInetAddress(user.getAddress());
|
||||||
TcpConnection connection = connections.get(address);
|
TcpConnection connection = connections.get(address);
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -104,6 +105,6 @@ public class PrivateChatController {
|
|||||||
if (user == null || user.getAddress() == null) {
|
if (user == null || user.getAddress() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return connections.containsKey(user.getAddress().getHostAddress());
|
return connections.containsKey(PeerIpv4.fromInetAddress(user.getAddress()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ import pitiupi.chat.net.ChatMessage;
|
|||||||
import pitiupi.chat.net.LegacyMpChatMessage;
|
import pitiupi.chat.net.LegacyMpChatMessage;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.presentation.shell.LocalUserIdentity;
|
import pitiupi.presentation.shell.LocalUserIdentity;
|
||||||
|
import pitiupi.presentation.shell.PeerIpv4;
|
||||||
import pitiupi.control.GuiManager;
|
import pitiupi.control.GuiManager;
|
||||||
import pitiupi.models.Messages.Message;
|
import pitiupi.models.Messages.Message;
|
||||||
import pitiupi.models.Plugin;
|
import pitiupi.models.Plugin;
|
||||||
@@ -138,17 +139,17 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
if (ip == null || ip.isBlank() || connectionManager == null) {
|
if (ip == null || ip.isBlank() || connectionManager == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
String needle = PeerIpv4.normalize(ip);
|
||||||
InetAddress address = InetAddress.getByName(ip);
|
if (needle.isEmpty()) {
|
||||||
for (UserPeerView view : connectionManager.getPresenceService().listPeerViews()) {
|
return null;
|
||||||
if (view.isSelf()) {
|
}
|
||||||
continue;
|
for (UserPeerView view : connectionManager.getPresenceService().listPeerViews()) {
|
||||||
}
|
if (view.isSelf()) {
|
||||||
if (view.getInetAddress().isPresent() && view.getInetAddress().get().equals(address)) {
|
continue;
|
||||||
return view.getInstanceId();
|
}
|
||||||
}
|
if (view.getInetAddress().map(PeerIpv4::fromInetAddress).orElse("").equals(needle)) {
|
||||||
|
return view.getInstanceId();
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -166,7 +167,7 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
privateChatController.receiveMessage(connection, legacy);
|
privateChatController.receiveMessage(connection, legacy);
|
||||||
String instanceId = peerInstanceIdForActivity(connection.getAddress().getHostAddress());
|
String instanceId = peerInstanceIdForActivity(PeerIpv4.fromInetAddress(connection.getAddress()));
|
||||||
if (instanceId != null) {
|
if (instanceId != null) {
|
||||||
connectionManager.getPeerActivityInbox().record(instanceId, getIdentifier(), "chat.private.tcp");
|
connectionManager.getPeerActivityInbox().record(instanceId, getIdentifier(), "chat.private.tcp");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import pitiupi.application.messaging.TransportMode;
|
|||||||
import pitiupi.application.presence.UserPeerView;
|
import pitiupi.application.presence.UserPeerView;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.presentation.shell.PeerDraftPanel;
|
import pitiupi.presentation.shell.PeerDraftPanel;
|
||||||
|
import pitiupi.presentation.shell.PeerIpv4;
|
||||||
import pitiupi.presentation.shell.PluginRoutingPanel;
|
import pitiupi.presentation.shell.PluginRoutingPanel;
|
||||||
import pitiupi.presentation.shell.PluginSessionKeys;
|
import pitiupi.presentation.shell.PluginSessionKeys;
|
||||||
import pitiupi.presentation.shell.PluginSessionWorkspace;
|
import pitiupi.presentation.shell.PluginSessionWorkspace;
|
||||||
@@ -350,14 +351,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String canonicalPeerIp(String ip) {
|
private static String canonicalPeerIp(String ip) {
|
||||||
if (ip == null || ip.isBlank()) {
|
return PeerIpv4.normalize(ip);
|
||||||
return "";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return InetAddress.getByName(ip.trim()).getHostAddress();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return ip.trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void focusMulticastSharing() {
|
public void focusMulticastSharing() {
|
||||||
@@ -375,7 +369,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
peer.getInetAddress().ifPresent(addr -> {
|
peer.getInetAddress().ifPresent(addr -> {
|
||||||
String ip = addr.getHostAddress();
|
String ip = PeerIpv4.fromInetAddress(addr);
|
||||||
openPeerTab(ip, peer.getDisplayName());
|
openPeerTab(ip, peer.getDisplayName());
|
||||||
String key = PluginSessionKeys.peer(canonicalPeerIp(ip));
|
String key = PluginSessionKeys.peer(canonicalPeerIp(ip));
|
||||||
sharingByTabKey.put(key, true);
|
sharingByTabKey.put(key, true);
|
||||||
@@ -389,7 +383,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
if (peer == null || peer.isBroadcastTarget() || peer.isSelf()) {
|
if (peer == null || peer.isBroadcastTarget() || peer.isSelf()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
peer.getInetAddress().ifPresent(addr -> openPeerTab(addr.getHostAddress(), peer.getDisplayName()));
|
peer.getInetAddress().ifPresent(addr -> openPeerTab(PeerIpv4.fromInetAddress(addr), peer.getDisplayName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureTodosTab() {
|
private void ensureTodosTab() {
|
||||||
@@ -534,7 +528,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (senderName != null && senderName.equals(view.getDisplayName())) {
|
if (senderName != null && senderName.equals(view.getDisplayName())) {
|
||||||
view.getInetAddress().ifPresent(addr -> peerIpsByViewKey.put(viewKey, addr.getHostAddress()));
|
view.getInetAddress().ifPresent(addr -> peerIpsByViewKey.put(viewKey, PeerIpv4.fromInetAddress(addr)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user