Fix #3940: 修复未正确解析 IPv6 服务器地址的问题 (#3942)

This commit is contained in:
Glavo
2025-05-29 19:34:53 +08:00
committed by GitHub
parent f223d2bc41
commit e0805fc25f
5 changed files with 215 additions and 71 deletions

View File

@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.ServerAddress;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -295,15 +296,21 @@ public class DefaultLauncher extends Launcher {
res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getGame(), configuration, features));
if (StringUtils.isNotBlank(options.getServerIp())) {
String[] args = options.getServerIp().split(":");
if (GameVersionNumber.asGameVersion(gameVersion).compareTo("1.20") < 0) {
res.add("--server");
res.add(args[0]);
res.add("--port");
res.add(args.length > 1 ? args[1] : "25565");
} else {
res.add("--quickPlayMultiplayer");
res.add(args[0] + ":" + (args.length > 1 ? args[1] : "25565"));
String address = options.getServerIp();
try {
ServerAddress parsed = ServerAddress.parse(address);
if (GameVersionNumber.asGameVersion(gameVersion).compareTo("1.20") < 0) {
res.add("--server");
res.add(parsed.getHost());
res.add("--port");
res.add(parsed.getPort() >= 0 ? String.valueOf(parsed.getPort()) : "25565");
} else {
res.add("--quickPlayMultiplayer");
res.add(parsed.getPort() < 0 ? address + ":25565" : address);
}
} catch (IllegalArgumentException e) {
LOG.warning("Invalid server address: " + address, e);
}
}

View File

@@ -0,0 +1,125 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* @author Glavo
*/
public final class ServerAddress {
private static final int UNKNOWN_PORT = -1;
private static IllegalArgumentException illegalAddress(String address) {
return new IllegalArgumentException("Invalid server address: " + address);
}
/**
* @throws IllegalArgumentException if the address is not a valid server address
*/
public static @NotNull ServerAddress parse(@NotNull String address) {
Objects.requireNonNull(address);
if (!address.startsWith("[")) {
int colonPos = address.indexOf(':');
if (colonPos >= 0) {
if (colonPos == address.length() - 1)
throw illegalAddress(address);
String host = address.substring(0, colonPos);
int port;
try {
port = Integer.parseInt(address.substring(colonPos + 1));
} catch (NumberFormatException e) {
throw illegalAddress(address);
}
if (port < 0 || port > 0xFFFF)
throw illegalAddress(address);
return new ServerAddress(host, port);
} else {
return new ServerAddress(address);
}
} else {
// Parse IPv6 address
int colonIndex = address.indexOf(':');
int closeBracketIndex = address.lastIndexOf(']');
if (colonIndex < 0 || closeBracketIndex < colonIndex)
throw illegalAddress(address);
String host = address.substring(1, closeBracketIndex);
if (closeBracketIndex == address.length() - 1)
return new ServerAddress(host);
if (address.length() < closeBracketIndex + 3 || address.charAt(closeBracketIndex + 1) != ':')
throw illegalAddress(address);
int port;
try {
port = Integer.parseInt(address.substring(closeBracketIndex + 2));
} catch (NumberFormatException e) {
throw illegalAddress(address);
}
if (port < 0 || port > 0xFFFF)
throw illegalAddress(address);
return new ServerAddress(host, port);
}
}
private final String host;
private final int port;
public ServerAddress(@NotNull String host) {
this(host, UNKNOWN_PORT);
}
public ServerAddress(@NotNull String host, int port) {
this.host = Objects.requireNonNull(host);
this.port = port;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ServerAddress)) return false;
ServerAddress that = (ServerAddress) o;
return port == that.port && Objects.equals(host, that.host);
}
@Override
public int hashCode() {
return Objects.hash(host, port);
}
@Override
public String toString() {
return String.format("ServerAddress[host='%s', port=%d]", host, port);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @author Glavo
*/
public final class ServerAddressTest {
@Test
public void testParse() {
assertEquals(new ServerAddress("example.com"), ServerAddress.parse("example.com"));
assertEquals(new ServerAddress("example.com", 25565), ServerAddress.parse("example.com:25565"));
assertEquals(new ServerAddress("127.0.0.0"), ServerAddress.parse("127.0.0.0"));
assertEquals(new ServerAddress("127.0.0.0", 0), ServerAddress.parse("127.0.0.0:0"));
assertEquals(new ServerAddress("127.0.0.0", 12345), ServerAddress.parse("127.0.0.0:12345"));
assertEquals(new ServerAddress("::1"), ServerAddress.parse("[::1]"));
assertEquals(new ServerAddress("::1", 0), ServerAddress.parse("[::1]:0"));
assertEquals(new ServerAddress("::1", 12345), ServerAddress.parse("[::1]:12345"));
assertEquals(new ServerAddress("2001:db8::1"), ServerAddress.parse("[2001:db8::1]"));
assertEquals(new ServerAddress("2001:db8::1", 0), ServerAddress.parse("[2001:db8::1]:0"));
assertEquals(new ServerAddress("2001:db8::1", 12345), ServerAddress.parse("[2001:db8::1]:12345"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("["));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[]]"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[]:0"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[::1]:"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[::1]|"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[::1]|0"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[::1]:a"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[::1]:65536"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[::1]:-1"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[ ]:-1"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("[-]:-1"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("example.com:"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("example.com:a"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("example.com:65536"));
assertThrows(IllegalArgumentException.class, () -> ServerAddress.parse("example.com:-1"));
}
}