diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml index cda68c6532..a44c2607ac 100644 --- a/.github/workflows/pr-pipeline.yml +++ b/.github/workflows/pr-pipeline.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: - java-version: '16' + java-version: '17' distribution: 'temurin' - name: Build with Maven (compile -> test -> package) run: mvn -B package --file pom.xml diff --git a/Dockerfile b/Dockerfile index c286375f60..d65c5576e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # # Cosmic JAR creation stage # -FROM maven:3.8.1-openjdk-16 AS jar +FROM maven:3.8.4-openjdk-17 AS jar # Build in a separated location which won't have permissions issues. WORKDIR /opt/cosmic @@ -21,7 +21,7 @@ RUN mvn -f ./pom.xml clean package -Dmaven.test.skip -T 1C # # Server creation stage # -FROM openjdk:16 +FROM openjdk:17.0.2 # Host the server in a location that won't have permissions issues. WORKDIR /opt/server diff --git a/pom.xml b/pom.xml index 0857beffb6..2d53675e85 100644 --- a/pom.xml +++ b/pom.xml @@ -11,45 +11,58 @@ jar + UTF-8 - 16 + 17 ${java.version} ${java.version} net.server.Server - 2.17.1 - 21.1.0 - 4.1.67.Final - 5.7.2 + + 3.0.0-M5 + 3.2.0 + 3.3.0 + + + 1.7.36 + 2.17.1 + 21.1.0 + 4.1.74.Final + 5.8.2 + 1.15 + 1.0 + 2.11.0 + 5.0.1 + 8.0.28 com.esotericsoftware.yamlbeans yamlbeans - 1.15 + ${yamlbeans.version} net.jcip jcip-annotations - 1.0 + ${jcip-annotations.version} commons-io commons-io - 2.11.0 + ${commons-io.version} com.zaxxer HikariCP - 5.0.0 + ${HikariCP.version} mysql mysql-connector-java - 8.0.26 + ${mysql-connector-java.version} @@ -78,7 +91,7 @@ org.slf4j slf4j-api - 1.7.32 + ${slf4j-api.version} org.apache.logging.log4j @@ -120,7 +133,6 @@ ${junit.version} - @@ -129,7 +141,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + ${maven-jar-plugin.version} default-jar @@ -141,14 +153,14 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + ${maven-surefire-plugin.version} org.apache.maven.plugins maven-assembly-plugin - 3.3.0 + ${maven-assembly-plugin.version} jar-with-dependencies diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index 12ca7278f9..5ed36fbcb7 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -1335,7 +1335,7 @@ public class Client extends ChannelInboundHandlerAdapter { try { MessageDigest digester = MessageDigest.getInstance(type); digester.update(password.getBytes(StandardCharsets.UTF_8), 0, password.length()); - return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash); + return HexTool.toHexString(digester.digest()).replace(" ", "").toLowerCase().equals(hash); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Encoding the string failed", e); } diff --git a/src/main/java/client/command/commands/gm3/PeCommand.java b/src/main/java/client/command/commands/gm3/PeCommand.java index 41978e7da8..da2af746c3 100644 --- a/src/main/java/client/command/commands/gm3/PeCommand.java +++ b/src/main/java/client/command/commands/gm3/PeCommand.java @@ -64,7 +64,7 @@ public class PeCommand extends Command { } - byte[] packetContent = HexTool.getByteArrayFromHexString(packet); + byte[] packetContent = HexTool.toBytes(packet); InPacket inPacket = new ByteBufInPacket(Unpooled.wrappedBuffer(packetContent)); short packetId = inPacket.readShort(); final PacketHandler packetHandler = PacketProcessor.getProcessor(0, c.getChannel()).getHandler(packetId); diff --git a/src/main/java/net/encryption/MapleAESOFB.java b/src/main/java/net/encryption/MapleAESOFB.java index f55b2a277f..4de36a078b 100644 --- a/src/main/java/net/encryption/MapleAESOFB.java +++ b/src/main/java/net/encryption/MapleAESOFB.java @@ -180,7 +180,7 @@ public class MapleAESOFB { @Override public String toString() { - return "IV: " + HexTool.toString(this.iv); + return "IV: " + HexTool.toHexString(this.iv); } private static byte[] funnyShit(byte inputByte, byte[] in) { diff --git a/src/main/java/net/packet/logging/InPacketLogger.java b/src/main/java/net/packet/logging/InPacketLogger.java index da9e8aa16d..adf48616a6 100644 --- a/src/main/java/net/packet/logging/InPacketLogger.java +++ b/src/main/java/net/packet/logging/InPacketLogger.java @@ -35,9 +35,9 @@ public class InPacketLogger extends ChannelInboundHandlerAdapter implements Pack final String opcodeName = getRecvOpcodeName(opcode); final String prefix = opcodeName == null ? " " : ""; log.debug("{}ClientSend:{} [{}] ({}) {} {}", prefix, opcodeName, opcodeHex, packetLength, - HexTool.toString(content), HexTool.toStringFromAscii(content)); + HexTool.toHexString(content), HexTool.toStringFromAscii(content)); } else { - log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + "..."); + log.debug(HexTool.toHexString(new byte[]{content[0], content[1]}) + "..."); } } diff --git a/src/main/java/net/packet/logging/MonitoredChrLogger.java b/src/main/java/net/packet/logging/MonitoredChrLogger.java index dc3785e550..782e78c794 100644 --- a/src/main/java/net/packet/logging/MonitoredChrLogger.java +++ b/src/main/java/net/packet/logging/MonitoredChrLogger.java @@ -75,7 +75,7 @@ public class MonitoredChrLogger { return; } - String packet = packetContent.length > 0 ? HexTool.toString(packetContent) : ""; + String packet = packetContent.length > 0 ? HexTool.toHexString(packetContent) : ""; log.info("{}-{} {}-{}", c.getAccountName(), chr.getName(), packetId, packet); } diff --git a/src/main/java/net/packet/logging/OutPacketLogger.java b/src/main/java/net/packet/logging/OutPacketLogger.java index ea8a96356e..5de9307afd 100644 --- a/src/main/java/net/packet/logging/OutPacketLogger.java +++ b/src/main/java/net/packet/logging/OutPacketLogger.java @@ -36,9 +36,9 @@ public class OutPacketLogger extends ChannelOutboundHandlerAdapter implements Pa String opcodeName = getSendOpcodeName(opcode); String prefix = opcodeName == null ? " " : ""; log.debug("{}ServerSend:{} [{}] ({}) {} {}", prefix, opcodeName, opcodeHex, packetLength, - HexTool.toString(content), HexTool.toStringFromAscii(content)); + HexTool.toHexString(content), HexTool.toStringFromAscii(content)); } else { - log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + " ..."); + log.debug(HexTool.toHexString(new byte[]{content[0], content[1]}) + " ..."); } } diff --git a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java index 7d07dcc46d..4dbbae373b 100644 --- a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java +++ b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java @@ -50,7 +50,7 @@ public final class LoginPasswordHandler implements PacketHandler { private static String hashpwSHA512(String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest digester = MessageDigest.getInstance("SHA-512"); digester.update(pwd.getBytes(StandardCharsets.UTF_8), 0, pwd.length()); - return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase(); + return HexTool.toHexString(digester.digest()).replace(" ", "").toLowerCase(); } @Override @@ -67,7 +67,7 @@ public final class LoginPasswordHandler implements PacketHandler { p.skip(6); // localhost masked the initial part with zeroes... byte[] hwidNibbles = p.readBytes(4); - Hwid hwid = new Hwid(HexTool.bytesToHex(hwidNibbles)); + Hwid hwid = new Hwid(HexTool.toCompactHexString(hwidNibbles)); int loginok = c.login(login, pwd, hwid); diff --git a/src/main/java/tools/HexTool.java b/src/main/java/tools/HexTool.java index 03ae30e755..64b7959a0a 100644 --- a/src/main/java/tools/HexTool.java +++ b/src/main/java/tools/HexTool.java @@ -22,94 +22,68 @@ package tools; import constants.string.CharsetConstants; -import io.netty.buffer.ByteBufUtil; -import java.io.ByteArrayOutputStream; +import java.util.HexFormat; +/** + * Handles converting back and forth from byte arrays to hex strings. + */ public class HexTool { - private static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - private static String toString(byte byteValue) { - int tmp = byteValue << 8; - char[] retstr = new char[]{HEX[(tmp >> 12) & 0x0F], HEX[(tmp >> 8) & 0x0F]}; - return String.valueOf(retstr); - } - - public static String toString(byte[] bytes) { - StringBuilder hexed = new StringBuilder(); - for (byte aByte : bytes) { - hexed.append(toString(aByte)); - hexed.append(' '); - } - return hexed.substring(0, hexed.length() - 1); - } - - public static String toCompressedString(byte[] bytes) { - StringBuilder hexed = new StringBuilder(); - for (byte aByte : bytes) { - hexed.append(toString(aByte)); - } - return hexed.substring(0, hexed.length()); - } - - public static byte[] getByteArrayFromHexString(String hex) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int nexti = 0; - int nextb = 0; - boolean highoc = true; - outer: - for (; ; ) { - int number = -1; - while (number == -1) { - if (nexti == hex.length()) { - break outer; - } - char chr = hex.charAt(nexti); - if (chr >= '0' && chr <= '9') { - number = chr - '0'; - } else if (chr >= 'a' && chr <= 'f') { - number = chr - 'a' + 10; - } else if (chr >= 'A' && chr <= 'F') { - number = chr - 'A' + 10; - } else { - number = -1; - } - nexti++; - } - if (highoc) { - nextb = number << 4; - highoc = false; - } else { - nextb |= number; - highoc = true; - baos.write(nextb); - } - } - return baos.toByteArray(); - } - - public static String toStringFromAscii(final byte[] bytes) { - byte[] ret = new byte[bytes.length]; - for (int x = 0; x < bytes.length; x++) { - if (bytes[x] < 32 && bytes[x] >= 0) { - ret[x] = '.'; - } else { - int chr = ((short) bytes[x]) & 0xFF; - ret[x] = (byte) chr; - } - } - - return new String(ret, CharsetConstants.CHARSET); + /** + * Convert a byte array to its hex string representation (upper case). + * Each byte value is converted to two hex characters delimited by a space. + * + * @param bytes Byte array to convert to a hex string. + * Example: {1, 16, 127, -1} is converted to "01 F0 7F FF" + * @return The hex string + */ + public static String toHexString(byte[] bytes) { + return HexFormat.ofDelimiter(" ").withUpperCase().formatHex(bytes); } /** - * Get upper case hex dump + * Convert a byte array to its hex string representation (upper case). + * Like {@link #toHexString(byte[]) HexTool.toString}, but with no space delimiter. + * + * @return The compact hex string */ - public static String bytesToHex(byte[] bytes) { - return ByteBufUtil.hexDump(bytes).toUpperCase(); + public static String toCompactHexString(byte[] bytes) { + return HexFormat.of().withUpperCase().formatHex(bytes); } - public static byte[] hexToBytes(String hex) { - return ByteBufUtil.decodeHexDump(hex); + /** + * Convert a hex string to its byte array representation. Two consecutive hex characters are converted to one byte. + * + * @param hexString Hex string to convert to bytes. May be lower or upper case, and hex character pairs may be + * delimited by a space or not. + * Example: "01 10 7F FF" is converted to {1, 16, 127, -1}. + * The following hex strings are considered identical and are converted to the same byte array: + * "01 10 7F FF", "01107FFF", "01 10 7f ff", "01107fff" + * @return The byte array + */ + public static byte[] toBytes(String hexString) { + return HexFormat.of().parseHex(removeAllSpaces(hexString)); + } + + private static String removeAllSpaces(String input) { + return input.replaceAll("\\s", ""); + } + + public static String toStringFromAscii(final byte[] bytes) { + byte[] filteredBytes = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + if (isSpecialCharacter(bytes[i])) { + filteredBytes[i] = '.'; + } else { + filteredBytes[i] = (byte) (bytes[i] & 0xFF); + } + } + + return new String(filteredBytes, CharsetConstants.CHARSET); + } + + private static boolean isSpecialCharacter(byte asciiCode) { + return asciiCode >= 0 && asciiCode <= 31; } } diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index ba2d646fa2..24af096e62 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -3307,7 +3307,7 @@ public class PacketCreator { p.writeByte(msgType); p.writeByte(speaker); p.writeString(talk); - p.writeBytes(HexTool.getByteArrayFromHexString(endBytes)); + p.writeBytes(HexTool.toBytes(endBytes)); return p; } @@ -6887,7 +6887,7 @@ public class PacketCreator { public static Packet customPacket(String packet) { OutPacket p = new ByteBufOutPacket(); - p.writeBytes(HexTool.getByteArrayFromHexString(packet)); + p.writeBytes(HexTool.toBytes(packet)); return p; } diff --git a/src/test/java/tools/HexToolTest.java b/src/test/java/tools/HexToolTest.java index 8ea59c403a..0354ca9a97 100644 --- a/src/test/java/tools/HexToolTest.java +++ b/src/test/java/tools/HexToolTest.java @@ -2,21 +2,57 @@ package tools; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; class HexToolTest { @Test - void upperCaseHexToBytesAndBack() { - String hex = "A1B2C3"; - byte[] bytes = HexTool.hexToBytes(hex); - assertEquals(hex, HexTool.bytesToHex(bytes)); + void bytesToHexString() { + byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1}; + String expectedHexString = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 7F FF"; + assertEquals(expectedHexString, HexTool.toHexString(bytes)); } @Test - void mixedCaseHexToBytesAndBack() { - String hex = "aB5DaA"; - byte[] bytes = HexTool.hexToBytes(hex); - assertEquals(hex.toUpperCase(), HexTool.bytesToHex(bytes)); + void bytesToCompactHexString() { + byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1}; + String expectedHexString = "0102030405060708090A0B0C0D0E0F10117FFF"; + assertEquals(expectedHexString, HexTool.toCompactHexString(bytes)); + } + + @Test + void hexStringWithSpacesToBytes() { + String hexString = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 7F FF"; + byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1}; + assertArrayEquals(expectedBytes, HexTool.toBytes(hexString)); + } + + @Test + void compactHexStringToBytes() { + String hexString = "0102030405060708090A0B0C0D0E0F10117FFF"; + byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1}; + assertArrayEquals(expectedBytes, HexTool.toBytes(hexString)); + } + + @Test + void lowerCaseHexStringToBytes() { + String lowerCaseHexString = "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 7f ff"; + byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1}; + assertArrayEquals(expectedBytes, HexTool.toBytes(lowerCaseHexString)); + } + + @Test + void lowerCaseCompactHexStringToBytes() { + String hexString = "0102030405060708090a0b0c0d0e0f10117fff"; + byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1}; + assertArrayEquals(expectedBytes, HexTool.toBytes(hexString)); + } + + @Test + void toStringFromAscii() { + byte[] asciiBytes = new byte[]{1, 10, 20, 30, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 31}; + String expectedString = "....0123456789.."; + assertEquals(expectedString, HexTool.toStringFromAscii(asciiBytes)); } } \ No newline at end of file