beautify locosocket...

This commit is contained in:
NetRiceCake
2025-12-06 21:09:14 +09:00
parent 8e737a688c
commit cdbfeff1f4
7 changed files with 73 additions and 57 deletions

View File

@@ -4,7 +4,7 @@ import com.github.netricecake.kakao.structs.ChatRoom;
import com.github.netricecake.kakao.structs.Member; import com.github.netricecake.kakao.structs.Member;
import com.github.netricecake.kakao.structs.Message; import com.github.netricecake.kakao.structs.Message;
import com.github.netricecake.loco.LocoPacket; import com.github.netricecake.loco.LocoPacket;
import com.github.netricecake.loco.LocoSocektHandler; import com.github.netricecake.loco.LocoSocketHandler;
import com.github.netricecake.loco.packet.inbound.member.*; import com.github.netricecake.loco.packet.inbound.member.*;
import com.github.netricecake.loco.packet.inbound.message.MessageIn; import com.github.netricecake.loco.packet.inbound.message.MessageIn;
import com.github.netricecake.loco.packet.inbound.room.ChatInfoIn; import com.github.netricecake.loco.packet.inbound.room.ChatInfoIn;
@@ -18,7 +18,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.Getter; import lombok.Getter;
public class LocoSocketHandlerImpl extends LocoSocektHandler { public class LocoSocketHandlerImpl extends LocoSocketHandler {
@Getter @Getter
private TalkClient client; private TalkClient client;

View File

@@ -3,7 +3,7 @@ package com.github.netricecake.kakao;
import com.github.netricecake.kakao.exception.*; import com.github.netricecake.kakao.exception.*;
import com.github.netricecake.kakao.structs.ChatRoom; import com.github.netricecake.kakao.structs.ChatRoom;
import com.github.netricecake.loco.LocoPacket; import com.github.netricecake.loco.LocoPacket;
import com.github.netricecake.loco.LocoSocektHandler; import com.github.netricecake.loco.LocoSocketHandler;
import com.github.netricecake.loco.LocoSocket; import com.github.netricecake.loco.LocoSocket;
import com.github.netricecake.loco.packet.inbound.login.CheckInIn; import com.github.netricecake.loco.packet.inbound.login.CheckInIn;
import com.github.netricecake.loco.packet.inbound.login.GetConfIn; import com.github.netricecake.loco.packet.inbound.login.GetConfIn;
@@ -90,7 +90,7 @@ public class TalkClient {
bookingData = KakaoApi.getBookingData(loginData.userId); bookingData = KakaoApi.getBookingData(loginData.userId);
if (bookingData == null || bookingData.getStatus() != 0) throw new BookingFailedException(); if (bookingData == null || bookingData.getStatus() != 0) throw new BookingFailedException();
LocoSocket checkInSocket = new LocoSocket(bookingData.getAddr(), bookingData.getPort(), new LocoSocektHandler() { LocoSocket checkInSocket = new LocoSocket(bookingData.getAddr(), bookingData.getPort(), new LocoSocketHandler() {
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -181,7 +181,7 @@ public class TalkClient {
if (si.getStatus() != 0) return false; if (si.getStatus() != 0) return false;
final CompletableFuture<Integer> future = new CompletableFuture<>(); final CompletableFuture<Integer> future = new CompletableFuture<>();
postSocket = new LocoSocket(si.getVhost(), si.getPort(), new LocoSocektHandler() { postSocket = new LocoSocket(si.getVhost(), si.getPort(), new LocoSocketHandler() {
@Override @Override
public void onPacket(LocoPacket packet) { public void onPacket(LocoPacket packet) {
JsonObject jsonObject = BsonUtil.bsonToJsonObject(packet.getBody()); JsonObject jsonObject = BsonUtil.bsonToJsonObject(packet.getBody());

View File

@@ -21,11 +21,12 @@ import java.util.concurrent.*;
public class LocoSocket { public class LocoSocket {
@Getter @Getter
private String ip; private final String ip;
@Getter @Getter
private int port; private final int port;
private CryptoManager cryptoManager; @Getter
private final CryptoManager cryptoManager = new CryptoManager();;
private Channel channel; private Channel channel;
private EventLoopGroup eventLoopGroup; private EventLoopGroup eventLoopGroup;
@@ -33,28 +34,24 @@ public class LocoSocket {
@Getter @Getter
private boolean alive = false; private boolean alive = false;
@Getter private final LocoSocketHandler locoSocektHandler;
private Map<Integer, Future<LocoPacket>> waitList = new HashMap<>();
@Getter private final ExecutorService handlerPool;
private LocoSocektHandler locoSocektHandler;
@Getter private final Map<Integer, Future<LocoPacket>> waitList = new HashMap<>();
private ExecutorService handlerPool;
private int packetIdCounter = 1000; private int packetIdCounter = 1000;
public LocoSocket(String ip, int port, LocoSocektHandler locoSocektHandler, ExecutorService handlerPool) { public LocoSocket(String ip, int port, LocoSocketHandler locoSocektHandler, ExecutorService handlerPool) {
this.ip = ip; this.ip = ip;
this.port = port; this.port = port;
this.locoSocektHandler = locoSocektHandler; this.locoSocektHandler = locoSocektHandler;
this.handlerPool = handlerPool; this.handlerPool = handlerPool;
cryptoManager = new CryptoManager();
} }
public void connect() throws IOException { public void connect() throws IOException {
if (alive) throw new IOException("Already connected");
try { try {
byte[] handshakePacket = cryptoManager.generateHandshakeMessage();
eventLoopGroup = new MultiThreadIoEventLoopGroup(1, NioIoHandler.newFactory()); eventLoopGroup = new MultiThreadIoEventLoopGroup(1, NioIoHandler.newFactory());
Bootstrap bootstrap = new Bootstrap(); Bootstrap bootstrap = new Bootstrap();
bootstrap.remoteAddress(new InetSocketAddress(ip, port)) bootstrap.remoteAddress(new InetSocketAddress(ip, port))
@@ -70,21 +67,17 @@ public class LocoSocket {
}); });
channel = bootstrap.connect().sync().channel(); channel = bootstrap.connect().sync().channel();
alive = true; alive = true;
channel.writeAndFlush(handshakePacket).sync(); channel.writeAndFlush(cryptoManager.generateHandshakeMessage()).sync();
channel.pipeline().addLast(new SecureLayerCodec(cryptoManager)); channel.pipeline().addLast(new SecureLayerCodec(cryptoManager));
channel.pipeline().addLast(new LocoCodec(this)); channel.pipeline().addLast(new LocoCodec(locoSocektHandler, handlerPool, waitList));
handlerPool.execute(() -> { handlerPool.execute(locoSocektHandler::onConnect);
locoSocektHandler.onConnect();
});
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {
try { try {
channel.closeFuture().sync(); channel.closeFuture().sync();
eventLoopGroup.shutdownGracefully(); eventLoopGroup.shutdownGracefully();
handlerPool.execute(() -> { handlerPool.execute(locoSocektHandler::onDisconnect);
locoSocektHandler.onDisconnect();
});
alive = false; alive = false;
} catch (Exception e) { } catch (Exception e) {
handlerPool.execute(() -> { handlerPool.execute(() -> {
@@ -119,7 +112,9 @@ public class LocoSocket {
waitList.put(packetId, future); waitList.put(packetId, future);
channel.writeAndFlush(packet); channel.writeAndFlush(packet);
try { try {
return future.get(); LocoPacket result = future.get(5, TimeUnit.SECONDS);
waitList.remove(packetId);
return result;
} catch (Exception e) { } catch (Exception e) {
handlerPool.execute(() -> { handlerPool.execute(() -> {
locoSocektHandler.onError(e); locoSocektHandler.onError(e);
@@ -135,6 +130,7 @@ public class LocoSocket {
}); });
eventLoopGroup.shutdownGracefully(); eventLoopGroup.shutdownGracefully();
channel.close(); channel.close();
handlerPool.execute(locoSocektHandler::onDisconnect);
alive = false; alive = false;
} }

View File

@@ -0,0 +1,13 @@
package com.github.netricecake.loco;
public class LocoSocketHandler {
public void onPacket(LocoPacket packet) {}
public void onConnect() {}
public void onDisconnect() {}
public void onError(Exception e) {}
}

View File

@@ -1,24 +1,33 @@
package com.github.netricecake.loco.codec; package com.github.netricecake.loco.codec;
import com.github.netricecake.loco.LocoPacket; import com.github.netricecake.loco.LocoPacket;
import com.github.netricecake.loco.LocoSocket; import com.github.netricecake.loco.LocoSocketHandler;
import com.github.netricecake.loco.util.BsonUtil; import com.github.netricecake.loco.util.BsonUtil;
import com.github.netricecake.loco.util.ByteUtil; import com.github.netricecake.loco.util.ByteUtil;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec; import io.netty.handler.codec.MessageToMessageCodec;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
public class LocoCodec extends MessageToMessageCodec<byte[], LocoPacket> { public class LocoCodec extends MessageToMessageCodec<byte[], LocoPacket> {
private LocoPacket currentLocoPacket = null; private LocoPacket currentLocoPacket = null;
private byte[] buffer = new byte[0]; private byte[] buffer = new byte[0];
private final LocoSocket locoSocket; private final Map<Integer, Future<LocoPacket>> waitList;
public LocoCodec(LocoSocket locoSocekt) { private final LocoSocketHandler locoSocektHandler;
this.locoSocket = locoSocekt;
private final ExecutorService handlerPool;
public LocoCodec(LocoSocketHandler locoSocektHandler, ExecutorService handlerPool, Map<Integer, Future<LocoPacket>> waitList) {
this.locoSocektHandler = locoSocektHandler;
this.handlerPool = handlerPool;
this.waitList = waitList;
} }
@Override @Override
@@ -59,17 +68,16 @@ public class LocoCodec extends MessageToMessageCodec<byte[], LocoPacket> {
} }
if (currentLocoPacket.getBodyLength() > buffer.length) break; if (currentLocoPacket.getBodyLength() > buffer.length) break;
byte[] body = ByteUtil.sliceBytes(buffer, 0, currentLocoPacket.getBodyLength()); byte[] body = ByteUtil.sliceBytes(buffer, 0, currentLocoPacket.getBodyLength());
System.out.println(currentLocoPacket.getMethod()); //System.out.println(currentLocoPacket.getMethod());
System.out.println(BsonUtil.bsonToJson(body)); //System.out.println(BsonUtil.bsonToJson(body));
buffer = ByteUtil.sliceBytes(buffer, currentLocoPacket.getBodyLength(), buffer.length - currentLocoPacket.getBodyLength()); buffer = ByteUtil.sliceBytes(buffer, currentLocoPacket.getBodyLength(), buffer.length - currentLocoPacket.getBodyLength());
currentLocoPacket.setBody(body); currentLocoPacket.setBody(body);
if (locoSocket.getWaitList().containsKey(currentLocoPacket.getPacketId())) { if (waitList.containsKey(currentLocoPacket.getPacketId())) {
((CompletableFuture<LocoPacket>) locoSocket.getWaitList().get(currentLocoPacket.getPacketId())).complete(currentLocoPacket); ((CompletableFuture<LocoPacket>) waitList.get(currentLocoPacket.getPacketId())).complete(currentLocoPacket);
locoSocket.getWaitList().remove(currentLocoPacket.getPacketId());
} else { } else {
final LocoPacket p = currentLocoPacket; final LocoPacket p = currentLocoPacket;
locoSocket.getHandlerPool().execute(() -> { handlerPool.execute(() -> {
locoSocket.getLocoSocektHandler().onPacket(p); locoSocektHandler.onPacket(p);
}); });
} }
currentLocoPacket = null; currentLocoPacket = null;

View File

@@ -1,10 +1,12 @@
package com.github.netricecake.loco.crypto; package com.github.netricecake.loco.crypto;
import com.github.netricecake.loco.util.ByteUtil; import com.github.netricecake.loco.util.ByteUtil;
import lombok.Getter;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key; import java.security.Key;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PublicKey; import java.security.PublicKey;
@@ -13,18 +15,23 @@ import java.security.spec.X509EncodedKeySpec;
public class CryptoManager { public class CryptoManager {
public final static int HANDSHAKE_BODY_SIZE = 256; // ENCRYPTED KEY public final static int HANDSHAKE_BODY_SIZE = 256;
// Key share(RSA ES) spec
public final static String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; public final static String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
public final static int RSA_LOCO_HEADER = 16; public final static int RSA_LOCO_HEADER = 16;
public final static byte[] RSA_PUBLIC_KEY_BYTES = ByteUtil.hexStringToByteArray("30820120300D06092A864886F70D01010105000382010D00308201080282010100A3B076E8C445851F19A670C231AAC6DB42EFD09717D06048A5CC56906CD1AB27B9DF37FFD5017E7C13A1405B5D1C3E4879A6A499D3C618A72472B0B50CA5EF1EF6EEA70369D9413FE662D8E2B479A9F72142EE70CEE6C2AD12045D52B25C4A204A28968E37F0BA6A49EE3EC9F2AC7A65184160F22F62C43A4067CD8D2A6F13D9B8298AB002763D236C9D1879D7FCE5B8FA910882B21E15247E0D0A24791308E51983614402E9FA03057C57E9E178B1CC39FE67288EFC461945CBCAA11D1FCC123E750B861F0D447EBE3C115F411A42DC95DDB21DA42774A5BCB1DDF7FA5F10628010C74F36F31C40EFCFE289FD81BABA44A6556A6C301210414B6023C3F46371020103"); public final static byte[] RSA_PUBLIC_KEY_BYTES = ByteUtil.hexStringToByteArray("30820120300D06092A864886F70D01010105000382010D00308201080282010100A3B076E8C445851F19A670C231AAC6DB42EFD09717D06048A5CC56906CD1AB27B9DF37FFD5017E7C13A1405B5D1C3E4879A6A499D3C618A72472B0B50CA5EF1EF6EEA70369D9413FE662D8E2B479A9F72142EE70CEE6C2AD12045D52B25C4A204A28968E37F0BA6A49EE3EC9F2AC7A65184160F22F62C43A4067CD8D2A6F13D9B8298AB002763D236C9D1879D7FCE5B8FA910882B21E15247E0D0A24791308E51983614402E9FA03057C57E9E178B1CC39FE67288EFC461945CBCAA11D1FCC123E750B861F0D447EBE3C115F411A42DC95DDB21DA42774A5BCB1DDF7FA5F10628010C74F36F31C40EFCFE289FD81BABA44A6556A6C301210414B6023C3F46371020103");
// AES spec
public final static String AES_ALGORITHM = "AES/GCM/NoPadding"; public final static String AES_ALGORITHM = "AES/GCM/NoPadding";
public final static int AES_KEY_SIZE = 128; public final static int AES_KEY_SIZE = 128;
public final static int AES_NONCE_SIZE = 12; public final static int AES_NONCE_SIZE = 12;
public final static int AES_LOCO_HEADER = 3; public final static int AES_LOCO_HEADER = 3;
@Getter
private Key aesKey; private Key aesKey;
// nonce(iv) generator
private final SecureRandom generator = new SecureRandom(); private final SecureRandom generator = new SecureRandom();
public CryptoManager() { public CryptoManager() {
@@ -32,13 +39,11 @@ public class CryptoManager {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(AES_KEY_SIZE); keyGenerator.init(AES_KEY_SIZE);
aesKey = keyGenerator.generateKey(); aesKey = keyGenerator.generateKey();
} catch (Exception e) { } catch (Exception e) {}
e.printStackTrace();
}
} }
public CryptoManager(Key aesKey) { public CryptoManager(byte[] key) {
this.aesKey = aesKey; this.aesKey = new SecretKeySpec(key, "AES");
} }
public byte[] generateHandshakeMessage() { public byte[] generateHandshakeMessage() {
@@ -49,9 +54,7 @@ public class CryptoManager {
byte[] encryptedKey = cipher.doFinal(aesKey.getEncoded()); byte[] encryptedKey = cipher.doFinal(aesKey.getEncoded());
byte[] length = ByteUtil.intToByteArrayLE(HANDSHAKE_BODY_SIZE); byte[] length = ByteUtil.intToByteArrayLE(HANDSHAKE_BODY_SIZE);
return ByteUtil.concatBytes(length, ByteUtil.intToByteArrayLE(RSA_LOCO_HEADER), ByteUtil.intToByteArrayLE(AES_LOCO_HEADER), encryptedKey); return ByteUtil.concatBytes(length, ByteUtil.intToByteArrayLE(RSA_LOCO_HEADER), ByteUtil.intToByteArrayLE(AES_LOCO_HEADER), encryptedKey);
} catch (Exception e) { } catch (Exception e) {}
e.printStackTrace();
}
return new byte[0]; return new byte[0];
} }
@@ -62,11 +65,8 @@ public class CryptoManager {
generator.nextBytes(nonce); generator.nextBytes(nonce);
Cipher cipher = Cipher.getInstance(AES_ALGORITHM); Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(AES_KEY_SIZE, nonce)); cipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(AES_KEY_SIZE, nonce));
byte[] encryptedBody = cipher.doFinal(message); return ByteUtil.concatBytes(nonce, cipher.doFinal(message));
return ByteUtil.concatBytes(nonce, encryptedBody); } catch (Exception e) {}
} catch (Exception e) {
e.printStackTrace();
}
return new byte[0]; return new byte[0];
} }
@@ -76,9 +76,7 @@ public class CryptoManager {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM); Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(AES_KEY_SIZE, nonce)); cipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(AES_KEY_SIZE, nonce));
return cipher.doFinal(ByteUtil.sliceBytes(message, AES_NONCE_SIZE, message.length - nonce.length)); return cipher.doFinal(ByteUtil.sliceBytes(message, AES_NONCE_SIZE, message.length - nonce.length));
} catch (Exception e) { } catch (Exception e) {}
e.printStackTrace();
}
return new byte[0]; return new byte[0];
} }

View File

@@ -4,6 +4,7 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import org.bson.BsonBinaryReader; import org.bson.BsonBinaryReader;
import org.bson.BsonDocument;
import org.bson.RawBsonDocument; import org.bson.RawBsonDocument;
import org.bson.codecs.BsonDocumentCodec; import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.DecoderContext; import org.bson.codecs.DecoderContext;
@@ -16,7 +17,7 @@ public class BsonUtil {
private final static Gson gson = new Gson(); private final static Gson gson = new Gson();
public static byte[] jsonToBson(String json) { public static byte[] jsonToBson(String json) {
var rawBson = RawBsonDocument.parse(json); RawBsonDocument rawBson = RawBsonDocument.parse(json);
ByteBuffer buffer = rawBson.getByteBuffer().asNIO(); ByteBuffer buffer = rawBson.getByteBuffer().asNIO();
byte[] exactBytes = new byte[buffer.remaining()]; byte[] exactBytes = new byte[buffer.remaining()];
buffer.get(exactBytes); buffer.get(exactBytes);
@@ -28,7 +29,7 @@ public class BsonUtil {
} }
public static String bsonToJson(byte[] bson) { public static String bsonToJson(byte[] bson) {
var doc = bsonDocumentCodec.decode( BsonDocument doc = bsonDocumentCodec.decode(
new BsonBinaryReader(ByteBuffer.wrap(bson)), new BsonBinaryReader(ByteBuffer.wrap(bson)),
DecoderContext.builder().build() DecoderContext.builder().build()
); );