Skip to content

Commit

Permalink
Let's make it a bit more organised
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim203 committed Aug 20, 2024
1 parent d70a79d commit 3c19bde
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 139 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package com.rtm516.mcxboxbroadcast.core.webrtc;

import com.rtm516.mcxboxbroadcast.core.webrtc.encryption.BedrockEncryptionEncoder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import javax.crypto.SecretKey;
import org.bouncycastle.util.encoders.Hex;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
import org.cloudburstmc.protocol.bedrock.data.DisconnectFailReason;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.netty.BedrockPacketWrapper;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec_v3;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.DisconnectPacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackClientResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket;
import org.cloudburstmc.protocol.bedrock.packet.TransferPacket;
import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils;
import org.cloudburstmc.protocol.common.util.VarInts;
import pe.pi.sctp4j.sctp.SCTPByteStreamListener;
import pe.pi.sctp4j.sctp.SCTPStream;

public class MinecraftDataHandler implements SCTPByteStreamListener {
private final BedrockPacketCodec packetCodec = new BedrockPacketCodec_v3();
private final SCTPStream sctpStream;
private final BedrockCodec codec;
private final BedrockCodecHelper helper;

private BedrockEncryptionEncoder encryptionEncoder;

private ByteBuf concat;
private int expectedLength;

public MinecraftDataHandler(SCTPStream sctpStream, BedrockCodec codec) {
this.sctpStream = sctpStream;
this.codec = codec;
this.helper = codec.createHelper();
}

@Override
public void onMessage(SCTPStream sctpStream, byte[] bytes) {
try {
System.out.println("binary message (" + sctpStream.getLabel() + "): " + Hex.toHexString(bytes));
if (bytes.length == 0) {
throw new IllegalStateException("Expected at least 2 bytes");
}
//todo only do this if segmentcount > 0
var buf = Unpooled.buffer(bytes.length);
buf.writeBytes(bytes);

byte remainingSegments = buf.readByte();
if (concat == null) {
expectedLength = VarInts.readUnsignedInt(buf);
}

if (remainingSegments > 0) {
if (concat == null) {
concat = buf;
} else {
concat.writeBytes(buf);
}
return;
}

if (concat != null) {
concat.writeBytes(buf);
buf = concat;
concat = null;
}

// if (buf.readableBytes() != expectedLength) {
// System.out.println("expected " + expectedLength + " bytes but got " + buf.readableBytes());
// var disconnect = new DisconnectPacket();
// disconnect.setReason(DisconnectFailReason.BAD_PACKET);
// disconnect.setKickMessage("");
// sendPacket(disconnect, sctpStream);
// return;
// }

var packet = readPacket(buf);

if (!(packet instanceof LoginPacket)) {
System.out.println(packet);
}

if (packet instanceof RequestNetworkSettingsPacket) {
var networkSettings = new NetworkSettingsPacket();
networkSettings.setCompressionAlgorithm(PacketCompressionAlgorithm.ZLIB);
networkSettings.setCompressionThreshold(0);
sendPacket(networkSettings);
} else if (packet instanceof LoginPacket login) {
// Utils.validateAndEncryptConnection(this, login.getChain(), login.getExtra());

var disconnect = new DisconnectPacket();
disconnect.setReason(DisconnectFailReason.ZOMBIE_CONNECTION);
disconnect.setKickMessage("hhhhehehe");
sendPacket(disconnect);

// var status = new PlayStatusPacket();
// status.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
// sendPacket(status);
//
// var info = new ResourcePacksInfoPacket();
// sendPacket(info);
} else if (packet instanceof ResourcePackClientResponsePacket status) {
switch (status.getStatus()) {
case COMPLETED -> {
var transfer = new TransferPacket();
transfer.setAddress("test.geysermc.org");
transfer.setPort(19132);
sendPacket(transfer);
}
case HAVE_ALL_PACKS -> {
var stack = new ResourcePackStackPacket();
stack.setExperimentsPreviouslyToggled(false);
stack.setForcedToAccept(false);
stack.setGameVersion("*");
sendPacket(stack);
}
default -> {
var disconnect = new DisconnectPacket();
disconnect.setKickMessage("disconnectionScreen.resourcePack");
sendPacket(disconnect);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onMessage(SCTPStream sctpStream, String s) {
System.out.println("string message (" + sctpStream.getLabel() + "): " + s);
}

@Override
public void close(SCTPStream sctpStream) {
System.out.println("stream closed: " + sctpStream.getLabel());
}

public void sendPacket(BedrockPacket packet) {
try {
ByteBuf dataBuf = Unpooled.buffer(128);
int packetId = codec.getPacketDefinition(packet.getClass()).getId();
System.out.println("packet id: " + packetId);
packetCodec.encodeHeader(
dataBuf,
BedrockPacketWrapper.create(packetId, 0, 0, null, null)
);
codec.tryEncode(helper, dataBuf, packet);

if (encryptionEncoder != null) {
dataBuf = encryptionEncoder.encode(dataBuf);
}

int segmentCount = (int) Math.ceil(dataBuf.readableBytes() / 10_000f);
for (int remainingSegements = segmentCount - 1; remainingSegements >= 0; remainingSegements--) {
int segmentLength = (remainingSegements == 0 ? dataBuf.readableBytes() : 10_000);
var sendBuf = Unpooled.buffer(segmentLength + 1 + 5);
sendBuf.writeByte(remainingSegements);
VarInts.writeUnsignedInt(sendBuf, segmentLength);
sendBuf.writeBytes(dataBuf, segmentLength);

byte[] send = new byte[sendBuf.readableBytes()];
sendBuf.readBytes(send);
System.out.println("sending: " + Hex.toHexString(send));
sctpStream.send(send);
}
} catch (Exception e) {
e.printStackTrace();
}
}

private BedrockPacket readPacket(ByteBuf buf) {
BedrockPacketWrapper wrapper = BedrockPacketWrapper.create();
packetCodec.decodeHeader(buf, wrapper);
System.out.println("sender/target: " + wrapper.getSenderSubClientId() + " " + wrapper.getTargetSubClientId());
var packet = codec.tryDecode(helper, buf.slice(), wrapper.getPacketId());
// release it
wrapper.getHandle().recycle(wrapper);
return packet;
}

public void enableEncryption(SecretKey secretKey) {
encryptionEncoder = new BedrockEncryptionEncoder(secretKey, EncryptionUtils.createCipher(true, true, secretKey));
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
package com.rtm516.mcxboxbroadcast.core.webrtc;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bouncycastle.util.encoders.Hex;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.netty.BedrockPacketWrapper;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec_v3;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils;
import org.cloudburstmc.protocol.common.util.VarInts;
import pe.pi.sctp4j.sctp.Association;
import pe.pi.sctp4j.sctp.AssociationListener;
import pe.pi.sctp4j.sctp.SCTPByteStreamListener;
import pe.pi.sctp4j.sctp.SCTPStream;

import java.util.HashMap;
import java.util.Map;

public class SctpAssociationListener implements AssociationListener {
private final Map<String, SCTPStream> streams = new HashMap<>();
private final BedrockCodec codec = Bedrock_v712.CODEC;
private final BedrockPacketCodec packetCodec = new BedrockPacketCodec_v3();
private final BedrockCodecHelper helper = codec.createHelper();

@Override
public void onAssociated(Association association) {
System.out.println("Association associated: " + association.toString());
Expand All @@ -46,126 +22,14 @@ public void onDCEPStream(SCTPStream sctpStream, String label, int i) throws Exce
return;
}
System.out.println("Received DCEP SCTP stream: " + sctpStream.toString());
streams.put(sctpStream.getLabel(), sctpStream);

sctpStream.setSCTPStreamListener(new SCTPByteStreamListener() {
private ByteBuf concat;
private int expectedLength;

@Override
public void onMessage(SCTPStream sctpStream, byte[] bytes) {
try {
System.out.println("binary message (" + sctpStream.getLabel() + "): " + Hex.toHexString(bytes));
if (bytes.length == 0) {
throw new IllegalStateException("Expected at least 2 bytes");
}
//todo only do this if segmentcount > 0
var buf = Unpooled.buffer(bytes.length);
buf.writeBytes(bytes);

byte remainingSegments = buf.readByte();
if (concat == null) {
expectedLength = VarInts.readUnsignedInt(buf);
}

if (remainingSegments > 0) {
if (concat == null) {
concat = buf;
} else {
concat.writeBytes(buf);
}
return;
}

if (concat != null) {
concat.writeBytes(buf);
buf = concat;
concat = null;
}

// if (buf.readableBytes() != expectedLength) {
// System.out.println("expected " + expectedLength + " bytes but got " + buf.readableBytes());
// var disconnect = new DisconnectPacket();
// disconnect.setReason(DisconnectFailReason.BAD_PACKET);
// disconnect.setKickMessage("");
// sendPacket(disconnect, sctpStream);
// return;
// }

var packet = readPacket(buf);

System.out.println(packet);
if (packet instanceof RequestNetworkSettingsPacket) {
var networkSettings = new NetworkSettingsPacket();
networkSettings.setCompressionAlgorithm(PacketCompressionAlgorithm.ZLIB);
networkSettings.setCompressionThreshold(0);
sendPacket(networkSettings, sctpStream);
} else if (packet instanceof LoginPacket login) {
var result = EncryptionUtils.validateChain(login.getChain());
System.out.println("signed? " + result.signed());
}
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onMessage(SCTPStream sctpStream, String s) {
System.out.println("string message (" + sctpStream.getLabel() + "): " + s);
}

@Override
public void close(SCTPStream sctpStream) {
System.out.println("stream closed: " + sctpStream.getLabel());
}
});
if ("ReliableDataChannel".equals(label)) {
sctpStream.setSCTPStreamListener(new MinecraftDataHandler(sctpStream, Bedrock_v712.CODEC));
}
}

@Override
public void onRawStream(SCTPStream sctpStream) {
System.out.println("Received raw SCTP stream: " + sctpStream.toString());
}

private void sendPacket(BedrockPacket packet, String streamLabel) {
sendPacket(packet, streams.get(streamLabel));
}

private void sendPacket(BedrockPacket packet, SCTPStream stream) {
try {
ByteBuf dataBuf = Unpooled.buffer(128);
int packetId = codec.getPacketDefinition(packet.getClass()).getId();
System.out.println("packet id: " + packetId);
packetCodec.encodeHeader(
dataBuf,
BedrockPacketWrapper.create(packetId, 0, 0, null, null)
);
codec.tryEncode(helper, dataBuf, packet);

int segmentCount = (int) Math.ceil(dataBuf.readableBytes() / 10_000f);
for (int remainingSegements = segmentCount - 1; remainingSegements >= 0; remainingSegements--) {
int segmentLength = (remainingSegements == 0 ? dataBuf.readableBytes() : 10_000);
var sendBuf = Unpooled.buffer(segmentLength + 1 + 5);
sendBuf.writeByte(remainingSegements);
VarInts.writeUnsignedInt(sendBuf, segmentLength);
sendBuf.writeBytes(dataBuf, segmentLength);

byte[] send = new byte[sendBuf.readableBytes()];
sendBuf.readBytes(send);
System.out.println("sending: " + Hex.toHexString(send));
stream.send(send);
}
} catch (Exception e) {
e.printStackTrace();
}
}

private BedrockPacket readPacket(ByteBuf buf) {
BedrockPacketWrapper wrapper = BedrockPacketWrapper.create();
packetCodec.decodeHeader(buf, wrapper);
System.out.println("sender/target: " + wrapper.getSenderSubClientId() + " " + wrapper.getTargetSubClientId());
var packet = codec.tryDecode(helper, buf.slice(), wrapper.getPacketId());
// release it
wrapper.getHandle().recycle(wrapper);
return packet;
}
}
Loading

0 comments on commit 3c19bde

Please sign in to comment.