Add nearly full Eaglercraft server support

TODO: finish making Eaglercraft 1.5 skins show up on Eaglercraft 1.8
This commit is contained in:
ayunami2000 2023-10-02 14:48:42 -04:00
parent 8a154e833f
commit a5eac20833
19 changed files with 1389 additions and 56 deletions

View File

@ -10,5 +10,5 @@ repositories {
}
dependencies {
implementation files("libs/ViaProxy-3.0.21-SNAPSHOT+java8_PATCHED.jar")
implementation files("libs/ViaProxy-3.0.22-SNAPSHOT+java8.jar")
}

View File

@ -0,0 +1,357 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.viaversion.viaversion.util.ChatColorUtil;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.util.AttributeKey;
import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
import net.raphimc.viaproxy.util.logging.Logger;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
/**
* Copyright (c) 2022-2023 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class ConnectionHandshake {
private static final AttributeKey<Integer> serverVersKey = AttributeKey.newInstance("eag-server-vers");
private static final int protocolV2 = 2;
private static final int protocolV3 = 3;
public static void attemptHandshake(List<Object> out, Channel ch, ProxyConnection proxyConnection, String password) {
try {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
DataOutputStream d = new DataOutputStream(bao);
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_VERSION);
d.writeByte(2); // legacy protocol version
d.writeShort(2); // supported eagler protocols count
d.writeShort(protocolV2); // client supports v2
d.writeShort(protocolV3); // client supports v3
d.writeShort(1); // supported game protocols count
d.writeShort(proxyConnection.getServerVersion().getVersion()); // client supports this protocol
String clientBrand = "ViaProxy";
d.writeByte(clientBrand.length());
d.writeBytes(clientBrand);
String clientVers = ViaProxy.VERSION;
d.writeByte(clientVers.length());
d.writeBytes(clientVers);
d.writeBoolean(password != null);
String username = proxyConnection.getGameProfile().getName();
d.writeByte(username.length());
d.writeBytes(username);
out.add(new BinaryWebSocketFrame(ch.alloc().buffer(bao.size()).writeBytes(bao.toByteArray())));
} catch (Throwable t) {
Logger.LOGGER.error("Exception in handshake");
Logger.LOGGER.error(t);
}
}
public static void attemptHandshake2(Channel ch, byte[] read, ProxyConnection proxyConnection, String password) {
try {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
DataOutputStream d = new DataOutputStream(bao);
String username = proxyConnection.getGameProfile().getName();
DataInputStream di = new DataInputStream(new ByteArrayInputStream(read));
int type = di.read();
if (type == HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH) {
StringBuilder protocols = new StringBuilder();
int c = di.readShort();
for (int i = 0; i < c; ++i) {
if (i > 0) {
protocols.append(", ");
}
protocols.append("v").append(di.readShort());
}
StringBuilder games = new StringBuilder();
c = di.readShort();
for (int i = 0; i < c; ++i) {
if (i > 0) {
games.append(", ");
}
games.append("mc").append(di.readShort());
}
Logger.LOGGER.info("Incompatible client: v2 & mc" + proxyConnection.getServerVersion().getVersion());
Logger.LOGGER.info("Server supports: {}", protocols);
Logger.LOGGER.info("Server supports: {}", games);
int msgLen = di.read();
byte[] dat = new byte[msgLen];
di.read(dat);
String msg = new String(dat, StandardCharsets.UTF_8);
proxyConnection.kickClient(msg);
} else if (type == HandshakePacketTypes.PROTOCOL_SERVER_VERSION) {
int serverVers = di.readShort();
if (serverVers != protocolV2 && serverVers != protocolV3) {
Logger.LOGGER.info("Incompatible server version: {}", serverVers);
proxyConnection.kickClient(serverVers < protocolV2 ? "Outdated Server" : "Outdated Client");
return;
}
ch.attr(serverVersKey).set(serverVers);
int gameVers = di.readShort();
if (gameVers != proxyConnection.getServerVersion().getVersion()) {
Logger.LOGGER.info("Incompatible minecraft protocol version: {}", gameVers);
proxyConnection.kickClient("This server does not support " + proxyConnection.getServerVersion().getName() + "!");
return;
}
Logger.LOGGER.info("Server protocol: {}", serverVers);
int msgLen = di.read();
byte[] dat = new byte[msgLen];
di.read(dat);
String brandStr = asciiString(dat);
msgLen = di.read();
dat = new byte[msgLen];
di.read(dat);
String versionStr = asciiString(dat);
Logger.LOGGER.info("Server version: {}", versionStr);
Logger.LOGGER.info("Server brand: {}", brandStr);
int authType = di.read();
int saltLength = (int) di.readShort() & 0xFFFF;
byte[] salt = new byte[saltLength];
di.read(salt);
bao.reset();
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN);
d.writeByte(username.length());
d.writeBytes(username);
String requestedServer = "default";
d.writeByte(requestedServer.length());
d.writeBytes(requestedServer);
if (authType != 0 && password != null && !password.isEmpty()) {
if (authType == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) {
Logger.LOGGER.warn("Server is using insecure plaintext authentication");
d.writeByte(password.length() << 1);
d.writeChars(password);
} else if (authType == HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256) {
SHA256Digest digest = new SHA256Digest();
int passLen = password.length();
digest.update((byte) ((passLen >> 8) & 0xFF));
digest.update((byte) (passLen & 0xFF));
for (int i = 0; i < passLen; ++i) {
char codePoint = password.charAt(i);
digest.update((byte) ((codePoint >> 8) & 0xFF));
digest.update((byte) (codePoint & 0xFF));
}
digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_SAVE, 0, 32);
byte[] hashed = new byte[32];
digest.doFinal(hashed, 0);
digest.reset();
digest.update(hashed, 0, 32);
digest.update(salt, 0, 32);
digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32);
digest.doFinal(hashed, 0);
digest.reset();
digest.update(hashed, 0, 32);
digest.update(salt, 32, 32);
digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32);
digest.doFinal(hashed, 0);
d.writeByte(32);
d.write(hashed);
} else if (authType == HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) {
SHA256Digest digest = new SHA256Digest();
byte[] passwd = password.getBytes(StandardCharsets.UTF_8);
digest.update(passwd, 0, passwd.length);
byte[] hashed = new byte[32];
digest.doFinal(hashed, 0);
byte[] toHexAndSalt = new byte[64];
for (int i = 0; i < 32; ++i) {
toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF];
toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF];
}
digest.reset();
digest.update(toHexAndSalt, 0, 64);
digest.update(salt, 0, salt.length);
digest.doFinal(hashed, 0);
for (int i = 0; i < 32; ++i) {
toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF];
toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF];
}
d.writeByte(64);
d.write(toHexAndSalt);
} else {
Logger.LOGGER.error("Unsupported authentication type: {}", authType);
proxyConnection.kickClient(ChatColorUtil.COLOR_CHAR + "cUnsupported authentication type: " + authType + "\n\n" + ChatColorUtil.COLOR_CHAR + "7(Use a newer version of the client)");
return;
}
} else {
d.writeByte(0);
}
ch.writeAndFlush(new BinaryWebSocketFrame(ch.alloc().buffer(bao.size()).writeBytes(bao.toByteArray())));
} else if (type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
showError(proxyConnection, di, true);
}
} catch (Throwable t) {
Logger.LOGGER.error("Exception in handshake");
Logger.LOGGER.error(t);
}
}
public static void attemptHandshake3(Channel ch, byte[] read, ProxyConnection proxyConnection) {
try {
int serverVers = ch.attr(serverVersKey).get();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
DataOutputStream d = new DataOutputStream(bao);
int msgLen;
byte[] dat;
DataInputStream di = new DataInputStream(new ByteArrayInputStream(read));
int type = di.read();
if (type == HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN) {
msgLen = di.read();
dat = new byte[msgLen];
di.read(dat);
String serverUsername = asciiString(dat);
JsonObject json = new JsonObject();
json.addProperty("name", serverUsername);
UUID uuid = new UUID(di.readLong(), di.readLong());
json.addProperty("uuid", uuid.toString());
proxyConnection.setGameProfile(new GameProfile(uuid, serverUsername));
if (proxyConnection.getC2P().hasAttr(EaglercraftHandler.profileDataKey)) {
EaglercraftHandler.ProfileData profileData = proxyConnection.getC2P().attr(EaglercraftHandler.profileDataKey).get();
bao.reset();
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA);
d.writeByte(profileData.type.length());
d.writeBytes(profileData.type);
d.writeShort(profileData.data.length);
d.write(profileData.data);
ch.writeAndFlush(new BinaryWebSocketFrame(ch.alloc().buffer(bao.size()).writeBytes(bao.toByteArray())));
}
bao.reset();
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN);
ch.writeAndFlush(new BinaryWebSocketFrame(ch.alloc().buffer(bao.size()).writeBytes(bao.toByteArray())));
} else if (type == HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN) {
if (serverVers == protocolV2) {
msgLen = di.read();
} else {
msgLen = di.readUnsignedShort();
}
dat = new byte[msgLen];
di.read(dat);
String errStr = new String(dat, StandardCharsets.UTF_8);
proxyConnection.kickClient(TextComponentSerializer.V1_8.deserialize(errStr).asLegacyFormatString());
} else if (type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
showError(proxyConnection, di, serverVers == protocolV2);
}
} catch (Throwable t) {
Logger.LOGGER.error("Exception in handshake");
Logger.LOGGER.error(t);
}
}
public static void attemptHandshake4(Channel ch, byte[] read, ProxyConnection proxyConnection) {
try {
int serverVers = ch.attr(serverVersKey).get();
DataInputStream di = new DataInputStream(new ByteArrayInputStream(read));
int type = di.read();
if (type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
showError(proxyConnection, di, serverVers == protocolV2);
}
} catch (Throwable t) {
Logger.LOGGER.error("Exception in handshake");
Logger.LOGGER.error(t);
}
}
private static void showError(ProxyConnection proxyConnection, DataInputStream err, boolean v2) throws IOException {
int errorCode = err.read();
int msgLen = v2 ? err.read() : err.readUnsignedShort();
byte[] dat = new byte[msgLen];
err.read(dat);
String errStr = new String(dat, StandardCharsets.UTF_8);
Logger.LOGGER.info("Server Error Code {}: {}", errorCode, errStr);
if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_BLOCKED) {
proxyConnection.kickClient("Server Error Ratelimited (blocked)");
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) {
proxyConnection.kickClient("Server Error Ratelimited (locked)");
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) {
proxyConnection.kickClient("Server Error Message " + TextComponentSerializer.V1_8.deserialize(errStr).asLegacyFormatString());
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED) {
proxyConnection.kickClient("Server Error Authentication required " + TextComponentSerializer.V1_8.deserialize(errStr).asLegacyFormatString());
}else {
proxyConnection.kickClient("Server Error Code " + errorCode + "\n" + TextComponentSerializer.V1_8.deserialize(errStr).asLegacyFormatString());
}
}
private static final byte[] HEX = new byte[]{
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'
};
public static String asciiString(byte[] bytes) {
char[] str = new char[bytes.length];
for(int i = 0; i < bytes.length; ++i) {
str[i] = (char)((int) bytes[i] & 0xFF);
}
return new String(str);
}
}

View File

@ -0,0 +1,291 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import com.google.common.primitives.Ints;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.websocketx.*;
import net.raphimc.netminecraft.constants.MCPackets;
import net.raphimc.netminecraft.netty.connection.NetClient;
import net.raphimc.netminecraft.packet.PacketTypes;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ClientboundPackets1_5_2;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ServerboundPackets1_5_2;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class EaglerServerHandler extends MessageToMessageCodec<BinaryWebSocketFrame, ByteBuf> {
private final VersionEnum version;
private final String password;
private final NetClient proxyConnection;
private final Map<UUID, String> uuidStringMap = new HashMap<>();
private final List<UUID> skinsBeingFetched = new ArrayList<>();
private ByteBuf serverBoundPartialPacket = Unpooled.EMPTY_BUFFER;
private ByteBuf clientBoundPartialPacket = Unpooled.EMPTY_BUFFER;
public EaglerServerHandler(NetClient proxyConnection, String password) {
this.version = proxyConnection instanceof ProxyConnection ? ((ProxyConnection) proxyConnection).getServerVersion() : VersionEnum.r1_5_2;
this.password = password;
this.proxyConnection = proxyConnection;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ExceptionUtil.handleNettyException(ctx, cause, null);
}
private int handshakeState = 0;
@Override
public void encode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (version.isNewerThan(VersionEnum.r1_6_4)) {
if (handshakeState == 0) {
handshakeState = 1;
if (((ProxyConnection) proxyConnection).getGameProfile() == null) {
out.add(Unpooled.EMPTY_BUFFER);
ctx.close();
return;
}
ConnectionHandshake.attemptHandshake(out, ctx.channel(), (ProxyConnection) proxyConnection, password);
if (out.isEmpty()) {
out.add(Unpooled.EMPTY_BUFFER);
}
} else if (handshakeState < 4) {
out.add(Unpooled.EMPTY_BUFFER);
} else {
out.add(new BinaryWebSocketFrame(in.retain()));
}
} else {
ByteBuf bb = ctx.alloc().buffer(serverBoundPartialPacket.readableBytes() + in.readableBytes());
bb.writeBytes(serverBoundPartialPacket);
serverBoundPartialPacket.release();
serverBoundPartialPacket = Unpooled.EMPTY_BUFFER;
bb.writeBytes(in);
int readerIndex = 0;
try {
while (bb.isReadable()) {
readerIndex = bb.readerIndex();
ServerboundPackets1_5_2 pkt = ServerboundPackets1_5_2.getPacket(bb.readUnsignedByte());
pkt.getPacketReader().accept(null, bb);
int len = bb.readerIndex() - readerIndex;
ByteBuf packet = ctx.alloc().buffer(len);
bb.readerIndex(readerIndex);
bb.readBytes(packet, len);
encodeOld(ctx, packet, out);
}
} catch (Exception e) {
bb.readerIndex(readerIndex);
if (bb.readableBytes() > 65535) {
ctx.close();
out.add(Unpooled.EMPTY_BUFFER);
return;
}
serverBoundPartialPacket = ctx.alloc().buffer(bb.readableBytes());
serverBoundPartialPacket.writeBytes(bb);
}
}
if (out.isEmpty()) {
out.add(Unpooled.EMPTY_BUFFER);
}
}
public void encodeOld(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (handshakeState == 0) {
handshakeState = 1;
if (in.readableBytes() >= 2 && in.getUnsignedByte(0) == 2) {
in.setByte(1, in.getUnsignedByte(1) + 8);
}
}
if (in.readableBytes() >= 1 && in.getUnsignedByte(0) == 0xFD) {
return;
}
if (in.readableBytes() >= 3 && in.getUnsignedByte(0) == 250) {
in.skipBytes(1);
String tag;
byte[] msg;
try {
tag = Types1_6_4.STRING.read(in);
if (tag.equals("EAG|Skins-1.8")) {
msg = new byte[in.readShort()];
in.readBytes(msg);
if (msg.length == 0) {
throw new IOException("Zero-length packet recieved");
}
final int packetId = msg[0] & 0xFF;
switch (packetId) {
case 3: {
if (msg.length != 17) {
throw new IOException("Invalid length " + msg.length + " for skin request packet");
}
final UUID searchUUID = SkinPackets.bytesToUUID(msg, 1);
if (uuidStringMap.containsKey(searchUUID)) {
// skinsBeingFetched.add(searchUUID);
String name = uuidStringMap.get(searchUUID);
ByteBuf bb = ctx.alloc().buffer();
bb.writeByte((byte) 250);
Types1_6_4.STRING.write(bb, "EAG|FetchSkin"); // todo: get to work
bb.writeByte((byte) 0);
bb.writeByte((byte) 0);
bb.writeBytes(name.getBytes(StandardCharsets.UTF_8));
out.add(new BinaryWebSocketFrame(bb));
}
break;
}
case 6: {
break;
}
default: {
throw new IOException("Unknown packet type " + packetId);
}
}
return;
}
} catch (Exception ignored) {
}
in.resetReaderIndex();
}
out.add(new BinaryWebSocketFrame(in.retain()));
}
@Override
public void decode(ChannelHandlerContext ctx, BinaryWebSocketFrame in, List<Object> out) {
if (version.isNewerThan(VersionEnum.r1_6_4)) {
if (handshakeState == 0) {
out.add(Unpooled.EMPTY_BUFFER);
} else if (handshakeState == 1) {
handshakeState = 2;
ConnectionHandshake.attemptHandshake2(ctx.channel(), ByteBufUtil.getBytes(in.content()), (ProxyConnection) proxyConnection, password);
out.add(Unpooled.EMPTY_BUFFER);
} else if (handshakeState == 2) {
handshakeState = 3;
ConnectionHandshake.attemptHandshake3(ctx.channel(), ByteBufUtil.getBytes(in.content()), (ProxyConnection) proxyConnection);
ByteBuf bb = ctx.alloc().buffer();
PacketTypes.writeVarInt(bb, MCPackets.S2C_LOGIN_SUCCESS.getId(version.getVersion()));
PacketTypes.writeString(bb, ((ProxyConnection) proxyConnection).getGameProfile().getId().toString());
PacketTypes.writeString(bb, ((ProxyConnection) proxyConnection).getGameProfile().getName());
out.add(bb);
} else if (handshakeState == 3) {
handshakeState = 4;
ConnectionHandshake.attemptHandshake4(ctx.channel(), ByteBufUtil.getBytes(in.content()), (ProxyConnection) proxyConnection);
out.add(Unpooled.EMPTY_BUFFER);
} else {
if (in.content().getByte(0) == MCPackets.S2C_LOGIN_SUCCESS.getId(version.getVersion()) && in.content().getByte(1) == 0 && in.content().getByte(2) == 2) {
out.add(Unpooled.EMPTY_BUFFER);
return;
}
if (in.content().getByte(0) == 0) {
in.content().skipBytes(1);
PacketTypes.readVarInt(in.content());
if (in.content().readableBytes() > 0) {
in.content().setByte(0, 0x40);
}
in.content().resetReaderIndex();
}
out.add(in.content().retain());
}
} else {
ByteBuf bb = ctx.alloc().buffer(clientBoundPartialPacket.readableBytes() + in.content().readableBytes());
bb.writeBytes(clientBoundPartialPacket);
clientBoundPartialPacket.release();
clientBoundPartialPacket = Unpooled.EMPTY_BUFFER;
bb.writeBytes(in.content());
int readerIndex = 0;
try {
while (bb.isReadable()) {
readerIndex = bb.readerIndex();
ClientboundPackets1_5_2 pkt = ClientboundPackets1_5_2.getPacket(bb.readUnsignedByte());
pkt.getPacketReader().accept(null, bb);
int len = bb.readerIndex() - readerIndex;
ByteBuf packet = ctx.alloc().buffer(len);
bb.readerIndex(readerIndex);
bb.readBytes(packet, len);
decodeOld(ctx, packet, out);
}
} catch (Exception e) {
bb.readerIndex(readerIndex);
if (bb.readableBytes() > 65535) {
ctx.close();
out.add(Unpooled.EMPTY_BUFFER);
return;
}
clientBoundPartialPacket = ctx.alloc().buffer(bb.readableBytes());
clientBoundPartialPacket.writeBytes(bb);
}
}
if (out.isEmpty()) {
out.add(Unpooled.EMPTY_BUFFER);
}
}
public void decodeOld(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.getUnsignedByte(0) == 0x14) {
in.skipBytes(5);
try {
String name = Types1_6_4.STRING.read(in);
UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8));
uuidStringMap.put(uuid, name);
} catch (Exception ignored) {
}
in.resetReaderIndex();
}
if (in.getUnsignedByte(0) == 0xFD) {
in.writerIndex(0);
in.writeByte((byte) 0xCD);
in.writeByte((byte) 0x00);
ctx.writeAndFlush(new BinaryWebSocketFrame(in.retain()));
return;
}
if (!skinsBeingFetched.isEmpty() && in.readableBytes() >= 3 && in.getUnsignedByte(0) == 250) {
in.skipBytes(1);
String tag;
byte[] msg;
try {
tag = Types1_6_4.STRING.read(in);
// System.out.println(tag);
if (tag.equals("EAG|UserSkin")) {
msg = new byte[in.readShort()];
in.readBytes(msg);
System.out.println(msg.length);
byte[] res = new byte[msg.length - 1];
System.arraycopy(msg, 1, res, 0, res.length);
if (res.length == 8192) {
final int[] tmp1 = new int[2048];
final int[] tmp2 = new int[4096];
for (int i = 0; i < tmp1.length; ++i) {
tmp1[i] = Ints.fromBytes(res[i * 4 + 3], res[i * 4], res[i * 4 + 1], res[i * 4 + 2]);
}
SkinConverter.convert64x32to64x64(tmp1, tmp2);
res = new byte[16384];
for (int i = 0; i < tmp2.length; ++i) {
System.arraycopy(Ints.toByteArray(tmp2[i]), 0, res, i * 4, 4);
}
} else {
for (int j = 0; j < res.length; j += 4) {
final byte tmp3 = res[j + 3];
res[j + 3] = res[j + 2];
res[j + 2] = res[j + 1];
res[j + 1] = res[j];
res[j] = tmp3;
}
}
in.writerIndex(1);
Types1_6_4.STRING.write(in, "EAG|Skins-1.8");
byte[] data = SkinPackets.makeCustomResponse(skinsBeingFetched.remove(0), 0, res);
in.writeShort(data.length);
in.writeBytes(data);
}
} catch (Exception ignored) {
}
in.resetReaderIndex();
}
if (in.getByte(0) == (byte) 0x83 && in.getShort(1) != 358) {
return;
}
out.add(in.retain());
}
}

View File

@ -77,6 +77,10 @@ public class EaglerSkinHandler extends ChannelInboundHandlerAdapter {
}
try {
if ("EAG|MySkin".equals(tag)) {
if (!FunnyConfig.eaglerSkins) {
bb.release();
return;
}
if (!EaglerSkinHandler.skinCollection.containsKey(uuid)) {
final int t = msg[0] & 0xFF;
if (t < EaglerSkinHandler.SKIN_DATA_SIZE.length && msg.length == EaglerSkinHandler.SKIN_DATA_SIZE[t] + 1) {
@ -87,6 +91,10 @@ public class EaglerSkinHandler extends ChannelInboundHandlerAdapter {
return;
}
if ("EAG|MyCape".equals(tag)) {
if (!FunnyConfig.eaglerSkins) {
bb.release();
return;
}
if (!EaglerSkinHandler.capeCollection.containsKey(uuid)) {
final int t = msg[0] & 0xFF;
if (t < EaglerSkinHandler.CAPE_DATA_SIZE.length && msg.length == EaglerSkinHandler.CAPE_DATA_SIZE[t] + 2) {
@ -113,7 +121,7 @@ public class EaglerSkinHandler extends ChannelInboundHandlerAdapter {
conc = conc2;
}
sendData(ctx, "EAG|UserSkin", conc);
} else if (EaglerXSkinHandler.skinService.loadPremiumSkins) {
} else if (FunnyConfig.premiumSkins) {
try {
URL url = new URL("https://playerdb.co/api/player/minecraft/" + fetch);
URLConnection urlConnection = url.openConnection();

View File

@ -78,6 +78,10 @@ public class EaglerVoiceHandler extends ChannelInboundHandlerAdapter {
super.channelRead(ctx, obj);
return;
}
if (!FunnyConfig.eaglerVoice) {
bb.release();
return;
}
final DataInputStream streamIn = new DataInputStream(new ByteArrayInputStream(msg));
final int sig = streamIn.read();
switch (sig) {

View File

@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class EaglerXSkinHandler extends ChannelInboundHandlerAdapter {
private final ConcurrentHashMap<String, byte[]> profileData;
public static final SkinService skinService;
public static SkinService skinService;
private String user;
private int pluginMessageId;
@ -63,15 +63,17 @@ public class EaglerXSkinHandler extends ChannelInboundHandlerAdapter {
}
if (this.user == null) {
this.user = ((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).username;
final UUID clientUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + this.user).getBytes(StandardCharsets.UTF_8));
if (this.profileData.containsKey("skin_v1")) {
try {
SkinPackets.registerEaglerPlayer(clientUUID, this.profileData.get("skin_v1"), EaglerXSkinHandler.skinService);
} catch (Throwable ex) {
if (FunnyConfig.eaglerSkins) {
final UUID clientUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + this.user).getBytes(StandardCharsets.UTF_8));
if (this.profileData.containsKey("skin_v1")) {
try {
SkinPackets.registerEaglerPlayer(clientUUID, this.profileData.get("skin_v1"), EaglerXSkinHandler.skinService);
} catch (Throwable ex) {
SkinPackets.registerEaglerPlayerFallback(clientUUID, EaglerXSkinHandler.skinService);
}
} else {
SkinPackets.registerEaglerPlayerFallback(clientUUID, EaglerXSkinHandler.skinService);
}
} else {
SkinPackets.registerEaglerPlayerFallback(clientUUID, EaglerXSkinHandler.skinService);
}
}
if (this.pluginMessageId <= 0) {
@ -101,8 +103,4 @@ public class EaglerXSkinHandler extends ChannelInboundHandlerAdapter {
EaglerXSkinHandler.skinService.unregisterPlayer(UUID.nameUUIDFromBytes(("OfflinePlayer:" + this.user).getBytes(StandardCharsets.UTF_8)));
}
}
static {
skinService = new SkinService();
}
}

View File

@ -15,6 +15,7 @@ import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.AttributeKey;
import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer;
import net.raphimc.netminecraft.constants.ConnectionState;
import net.raphimc.netminecraft.constants.MCPackets;
@ -24,6 +25,7 @@ import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ServerboundP
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
import net.raphimc.viaproxy.util.logging.Logger;
@ -38,6 +40,15 @@ import java.util.List;
import java.util.UUID;
public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, ByteBuf> {
public static class ProfileData {
public final String type;
public final byte[] data;
public ProfileData(String type, byte[] data) {
this.type = type;
this.data = data;
}
}
public static final AttributeKey<ProfileData> profileDataKey = AttributeKey.newInstance("eagx-profile-data");
private HostAndPort host;
public State state;
public VersionEnum version;
@ -50,7 +61,9 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(-2);
ctx.pipeline().remove("sizer");
if (ctx.pipeline().get(MCPipeline.SIZER_HANDLER_NAME) != null) {
ctx.pipeline().remove(MCPipeline.SIZER_HANDLER_NAME);
}
super.channelActive(ctx);
}
@ -224,12 +237,13 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
case LOGIN: {
final int packetId = data.readUnsignedByte();
if (packetId == 7) {
data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII);
String type = data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
final byte[] dataBytes = new byte[data.readUnsignedShort()];
data.readBytes(dataBytes);
if (data.isReadable()) {
throw new IllegalArgumentException("Too much data in packet: " + data.readableBytes() + " bytes");
}
ctx.channel().attr(profileDataKey).set(new ProfileData(type, dataBytes));
} else {
if (packetId != 8) {
throw new IllegalStateException("Unexpected packet id " + packetId + " in state " + this.state);
@ -244,8 +258,8 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
ctx.close();
return;
}
if (ctx.pipeline().get("legacy-passthrough-handler") != null) {
ctx.pipeline().remove("legacy-passthrough-handler");
if (ctx.pipeline().get(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME) != null) {
ctx.pipeline().remove(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME);
}
out.add(this.writeHandshake(ctx.alloc().buffer(), ConnectionState.LOGIN));
final ByteBuf loginHello = ctx.alloc().buffer();
@ -265,10 +279,12 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
ctx.channel().writeAndFlush(new BinaryWebSocketFrame(data.readerIndex(0).retain()));
break;
}
if (packetId == ServerboundPackets1_5_2.PLUGIN_MESSAGE.getId() && Types1_6_4.STRING.read(data).startsWith("EAG|")) {
break;
if (!ctx.channel().hasAttr(Main.secureWs)) {
if (packetId == ServerboundPackets1_5_2.PLUGIN_MESSAGE.getId() && Types1_6_4.STRING.read(data).startsWith("EAG|")) {
break;
}
}
} else if (this.version.isNewerThanOrEqualTo(VersionEnum.r1_7_2tor1_7_5)) {
} else if (this.version.isNewerThanOrEqualTo(VersionEnum.r1_7_2tor1_7_5) && !ctx.channel().hasAttr(Main.secureWs)) {
final int packetId = PacketTypes.readVarInt(data);
if (packetId == this.pluginMessageId && PacketTypes.readString(data, 32767).startsWith("EAG|")) {
break;
@ -295,8 +311,8 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
}
this.state = State.STATUS;
this.version = VersionEnum.r1_8;
if (ctx.pipeline().get("legacy-passthrough-handler") != null) {
ctx.pipeline().remove("legacy-passthrough-handler");
if (ctx.pipeline().get(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME) != null) {
ctx.pipeline().remove(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME);
}
out.add(this.writeHandshake(ctx.alloc().buffer(), ConnectionState.STATUS));
final ByteBuf statusRequest = ctx.alloc().buffer();
@ -312,7 +328,7 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
ctx.close();
return;
}
this.host = HostAndPort.fromString(handshake.requestHeaders().get("Host")).withDefaultPort(80);
this.host = HostAndPort.fromString(handshake.requestHeaders().get("Host").replaceAll("__", ".")).withDefaultPort(80);
}
super.userEventTriggered(ctx, evt);
}

View File

@ -1,7 +1,10 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
@ -9,14 +12,26 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import net.raphimc.netminecraft.constants.MCPipeline;
import net.raphimc.viaproxy.plugins.PluginManager;
import net.raphimc.viaproxy.plugins.events.Client2ProxyHandlerCreationEvent;
import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer;
import net.raphimc.viaproxy.proxy.client2proxy.passthrough.PassthroughClient2ProxyChannelInitializer;
import net.raphimc.viaproxy.proxy.client2proxy.passthrough.PassthroughClient2ProxyHandler;
import net.raphimc.viaproxy.proxy.client2proxy.passthrough.PassthroughInitialHandler;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class EaglercraftInitialHandler extends ByteToMessageDecoder {
private static SslContext sslContext;
private static final Method initChannelMethod;
public static SslContext sslContext;
@Override
protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) {
@ -27,7 +42,7 @@ public class EaglercraftInitialHandler extends ByteToMessageDecoder {
return;
}
if (in.readableBytes() >= 3 || in.getByte(0) != 71) {
if (in.readableBytes() >= 3 && in.getCharSequence(0, 3, StandardCharsets.UTF_8).equals("GET")) {
if ((in.readableBytes() >= 3 && in.getCharSequence(0, 3, StandardCharsets.UTF_8).equals("GET")) || (in.readableBytes() >= 4 && in.getCharSequence(0, 4, StandardCharsets.UTF_8).equals("POST"))) {
if (EaglercraftInitialHandler.sslContext != null) {
ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-ssl-handler", EaglercraftInitialHandler.sslContext.newHandler(ctx.alloc()));
}
@ -37,6 +52,43 @@ public class EaglercraftInitialHandler extends ByteToMessageDecoder {
ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-handler", new WebSocketServerProtocolHandler("/", null, true));
ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-active-notifier", new WebSocketActiveNotifier());
ctx.pipeline().addBefore("eaglercraft-initial-handler", "eaglercraft-handler", new EaglercraftHandler());
ctx.pipeline().replace(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME, Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME, new PassthroughInitialHandler() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
if (ctx.channel().isOpen()) {
if (msg.isReadable()) {
int lengthOrPacketId = msg.getUnsignedByte(0);
if (lengthOrPacketId == 0 || lengthOrPacketId == 1 || lengthOrPacketId == 2 || lengthOrPacketId == 254) {
boolean fard = false;
for (Map.Entry<String, ChannelHandler> entry : ctx.pipeline()) {
if (entry.getKey().equals(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME)) {
fard = true;
}
if (fard) {
ctx.pipeline().remove(entry.getValue());
}
}
Supplier<ChannelHandler> handlerSupplier = () -> PluginManager.EVENT_MANAGER.call(new Client2ProxyHandlerCreationEvent(new PassthroughClient2ProxyHandler(), true)).getHandler();
PassthroughClient2ProxyChannelInitializer channelInitializer = new PassthroughClient2ProxyChannelInitializer(handlerSupplier);
try {
initChannelMethod.invoke(channelInitializer, ctx.channel());
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
try {
((ChannelInboundHandler) ctx.pipeline().get(MCPipeline.HANDLER_HANDLER_NAME)).channelRead(ctx, msg.retain());
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
ctx.pipeline().remove(this);
ctx.pipeline().fireChannelRead(msg.retain());
}
}
}
}
});
ctx.fireUserEventTriggered(EaglercraftClientConnected.INSTANCE);
ctx.pipeline().fireChannelRead(in.readBytes(in.readableBytes()));
} else {
@ -55,14 +107,16 @@ public class EaglercraftInitialHandler extends ByteToMessageDecoder {
throw new RuntimeException("Failed to load SSL context", e);
}
}
try {
initChannelMethod = PassthroughClient2ProxyChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
initChannelMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public static final class EaglercraftClientConnected {
public static final EaglercraftClientConnected INSTANCE;
static {
INSTANCE = new EaglercraftClientConnected();
}
public static final EaglercraftClientConnected INSTANCE = new EaglercraftClientConnected();
}
@Override

View File

@ -9,7 +9,10 @@ import java.util.List;
import java.util.Map;
public class FunnyConfig extends Config {
private boolean premiumSkins = false;
public static boolean premiumSkins = false;
public static boolean eaglerUtils = true;
public static boolean eaglerSkins = true;
public static boolean eaglerVoice = true;
protected FunnyConfig(File configFile) {
super(configFile);
@ -17,14 +20,26 @@ public class FunnyConfig extends Config {
@Override
public URL getDefaultConfigURL() {
return Main.class.getResource("/eaglerskins.yml");
return Main.class.getResource("/vpeagutils.yml");
}
@Override
protected void handleConfig(Map<String, Object> map) {
Object item = map.get("premium-skins");
if (item instanceof Boolean) {
this.premiumSkins = (Boolean) item;
premiumSkins = (Boolean) item;
}
item = map.get("eagler-utils");
if (item instanceof Boolean) {
eaglerUtils = (Boolean) item;
}
item = map.get("eagler-skins");
if (item instanceof Boolean) {
eaglerSkins = (Boolean) item;
}
item = map.get("eagler-voice");
if (item instanceof Boolean) {
eaglerVoice = (Boolean) item;
}
}
@ -32,8 +47,4 @@ public class FunnyConfig extends Config {
public List<String> getUnsupportedOptions() {
return Collections.emptyList();
}
public boolean getPremiumSkins() {
return this.premiumSkins;
}
}

View File

@ -0,0 +1,108 @@
package me.ayunami2000.ayunViaProxyEagUtils;
/**
* base implementation of MD4 family style digest as outlined in "Handbook of
* Applied Cryptography", pages 344 - 347.
*/
public abstract class GeneralDigest {
private byte[] xBuf;
private int xBufOff;
private long byteCount;
/**
* Standard constructor
*/
protected GeneralDigest() {
xBuf = new byte[4];
xBufOff = 0;
}
/**
* Copy constructor. We are using copy constructors in place of the
* Object.clone() interface as this interface is not supported by J2ME.
*/
protected GeneralDigest(GeneralDigest t) {
xBuf = new byte[t.xBuf.length];
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
xBufOff = t.xBufOff;
byteCount = t.byteCount;
}
public void update(byte in) {
xBuf[xBufOff++] = in;
if (xBufOff == xBuf.length) {
processWord(xBuf, 0);
xBufOff = 0;
}
byteCount++;
}
public void update(byte[] in, int inOff, int len) {
//
// fill the current word
//
while ((xBufOff != 0) && (len > 0)) {
update(in[inOff]);
inOff++;
len--;
}
//
// process whole words.
//
while (len > xBuf.length) {
processWord(in, inOff);
inOff += xBuf.length;
len -= xBuf.length;
byteCount += xBuf.length;
}
//
// load in the remainder.
//
while (len > 0) {
update(in[inOff]);
inOff++;
len--;
}
}
public void finish() {
long bitLength = (byteCount << 3);
//
// add the pad bytes.
//
update((byte) 128);
while (xBufOff != 0) {
update((byte) 0);
}
processLength(bitLength);
processBlock();
}
public void reset() {
byteCount = 0;
xBufOff = 0;
for (int i = 0; i < xBuf.length; i++) {
xBuf[i] = 0;
}
}
protected abstract void processWord(byte[] in, int inOff);
protected abstract void processLength(long bitLength);
protected abstract void processBlock();
}

View File

@ -0,0 +1,63 @@
package me.ayunami2000.ayunViaProxyEagUtils;
/**
* Copyright (c) 2022-2023 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class HandshakePacketTypes {
public static final String AUTHENTICATION_REQUIRED = "Authentication Required:";
public static final int PROTOCOL_CLIENT_VERSION = 0x01;
public static final int PROTOCOL_SERVER_VERSION = 0x02;
public static final int PROTOCOL_VERSION_MISMATCH = 0x03;
public static final int PROTOCOL_CLIENT_REQUEST_LOGIN = 0x04;
public static final int PROTOCOL_SERVER_ALLOW_LOGIN = 0x05;
public static final int PROTOCOL_SERVER_DENY_LOGIN = 0x06;
public static final int PROTOCOL_CLIENT_PROFILE_DATA = 0x07;
public static final int PROTOCOL_CLIENT_FINISH_LOGIN = 0x08;
public static final int PROTOCOL_SERVER_FINISH_LOGIN = 0x09;
public static final int PROTOCOL_SERVER_ERROR = 0xFF;
public static final int STATE_OPENED = 0x00;
public static final int STATE_CLIENT_VERSION = 0x01;
public static final int STATE_CLIENT_LOGIN = 0x02;
public static final int STATE_CLIENT_COMPLETE = 0x03;
public static final int SERVER_ERROR_UNKNOWN_PACKET = 0x01;
public static final int SERVER_ERROR_INVALID_PACKET = 0x02;
public static final int SERVER_ERROR_WRONG_PACKET = 0x03;
public static final int SERVER_ERROR_EXCESSIVE_PROFILE_DATA = 0x04;
public static final int SERVER_ERROR_DUPLICATE_PROFILE_DATA = 0x05;
public static final int SERVER_ERROR_RATELIMIT_BLOCKED = 0x06;
public static final int SERVER_ERROR_RATELIMIT_LOCKED = 0x07;
public static final int SERVER_ERROR_CUSTOM_MESSAGE = 0x08;
public static final int SERVER_ERROR_AUTHENTICATION_REQUIRED = 0x09;
public static final int AUTH_METHOD_NONE = 0x0;
public static final int AUTH_METHOD_EAGLER_SHA256 = 0x01;
public static final int AUTH_METHOD_AUTHME_SHA256 = 0x02;
public static final int AUTH_METHOD_PLAINTEXT = 0xFF;
public static final byte[] EAGLER_SHA256_SALT_BASE = new byte[] { (byte) 117, (byte) 43, (byte) 1, (byte) 112,
(byte) 75, (byte) 3, (byte) 188, (byte) 61, (byte) 121, (byte) 31, (byte) 34, (byte) 181, (byte) 234,
(byte) 31, (byte) 247, (byte) 72, (byte) 12, (byte) 168, (byte) 138, (byte) 45, (byte) 143, (byte) 77,
(byte) 118, (byte) 245, (byte) 187, (byte) 242, (byte) 188, (byte) 219, (byte) 160, (byte) 235, (byte) 235,
(byte) 68 };
public static final byte[] EAGLER_SHA256_SALT_SAVE = new byte[] { (byte) 49, (byte) 25, (byte) 39, (byte) 38,
(byte) 253, (byte) 85, (byte) 70, (byte) 245, (byte) 71, (byte) 150, (byte) 253, (byte) 206, (byte) 4,
(byte) 26, (byte) 198, (byte) 249, (byte) 145, (byte) 251, (byte) 232, (byte) 174, (byte) 186, (byte) 98,
(byte) 27, (byte) 232, (byte) 55, (byte) 144, (byte) 83, (byte) 21, (byte) 36, (byte) 55, (byte) 170,
(byte) 118 };
}

View File

@ -1,28 +1,149 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AttributeKey;
import net.lenni0451.lambdaevents.EventHandler;
import net.raphimc.netminecraft.constants.MCPipeline;
import net.raphimc.netminecraft.netty.connection.NetClient;
import net.raphimc.netminecraft.util.ServerAddress;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.plugins.PluginManager;
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
import net.raphimc.viaproxy.plugins.events.Client2ProxyChannelInitializeEvent;
import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent;
import net.raphimc.viaproxy.plugins.events.types.ITyped;
import net.raphimc.viaproxy.proxy.session.LegacyProxyConnection;
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
import javax.net.ssl.*;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class Main extends ViaProxyPlugin {
public static final AttributeKey<Boolean> secureWs = AttributeKey.newInstance("eag-secure-ws");
public static final AttributeKey<String> wsPath = AttributeKey.newInstance("eag-ws-path");
public static final AttributeKey<String> eagxPass = AttributeKey.newInstance("eag-x-pass");
private static final SSLContext sc;
static {
try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
// Ignore differences between given hostname and certificate hostname
sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public void onEnable() {
PluginManager.EVENT_MANAGER.register(this);
(new FunnyConfig(new File("ViaLoader", "vpeagutils.yml"))).reloadConfig();
EaglerXSkinHandler.skinService = new SkinService();
}
@EventHandler
public void onEvent(final Proxy2ServerChannelInitializeEvent event) throws URISyntaxException {
if (event.getType() == ITyped.Type.POST) {
Channel ch = event.getChannel();
NetClient proxyConnection;
Channel c2p;
ServerAddress addr;
if (event.isLegacyPassthrough()) {
proxyConnection = LegacyProxyConnection.fromChannel(ch);
c2p = ((LegacyProxyConnection) proxyConnection).getC2P();
addr = ((LegacyProxyConnection) proxyConnection).getServerAddress();
} else {
proxyConnection = ProxyConnection.fromChannel(ch);
c2p = ((ProxyConnection) proxyConnection).getC2P();
addr = ((ProxyConnection) proxyConnection).getServerAddress();
}
c2p.attr(secureWs).set(true);
if (c2p.hasAttr(secureWs)) {
ch.attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(-2);
if (proxyConnection instanceof ProxyConnection && ((ProxyConnection) proxyConnection).getServerVersion().isNewerThan(VersionEnum.r1_6_4)) {
ch.pipeline().remove(MCPipeline.SIZER_HANDLER_NAME);
} else if (ch.pipeline().get(MCPipeline.ENCRYPTION_HANDLER_NAME) != null) {
ch.pipeline().remove(MCPipeline.ENCRYPTION_HANDLER_NAME);
}
StringBuilder url = new StringBuilder("ws");
boolean secure = c2p.attr(secureWs).get();
if (secure) {
final SSLEngine sslEngine = sc.createSSLEngine(addr.getAddress(), addr.getPort());
sslEngine.setUseClientMode(true);
sslEngine.setNeedClientAuth(false);
ch.pipeline().addFirst("eag-server-ssl", new SslHandler(sslEngine));
url.append("s");
ch.pipeline().addAfter("eag-server-ssl", "eag-server-http-codec", new HttpClientCodec());
} else {
ch.pipeline().addFirst("eag-server-http-codec", new HttpClientCodec());
}
url.append("://").append(addr.getAddress());
boolean addPort = (secure && addr.getPort() != 443) || (!secure && addr.getPort() != 80);
if (addPort) {
url.append(":").append(addr.getPort());
}
String path = c2p.attr(wsPath).get();
if (path != null) {
url.append("/").append(path);
}
URI uri = new URI(url.toString());
HttpHeaders headers = new DefaultHttpHeaders();
headers.set(HttpHeaderNames.HOST, uri.getHost() + (addPort ? ":" + uri.getPort() : ""));
headers.set(HttpHeaderNames.ORIGIN, "via.shhnowisnottheti.me");
ch.pipeline().addAfter("eag-server-http-codec", "eag-server-http-aggregator", new HttpObjectAggregator(2097152, true));
ch.pipeline().addAfter("eag-server-http-aggregator", "eag-server-ws-compression", WebSocketClientCompressionHandler.INSTANCE);
ch.pipeline().addAfter("eag-server-ws-compression", "eag-server-ws-handshaker", new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, null, true, headers, 2097152)));
ch.pipeline().addAfter("eag-server-ws-handshaker", "eag-server-ws-ready", new WebSocketConnectedNotifier());
ch.pipeline().addAfter("eag-server-ws-ready", "eag-server-handler", new EaglerServerHandler(proxyConnection, c2p.attr(eagxPass).get()));
}
}
}
@EventHandler
public void onEvent(final Client2ProxyChannelInitializeEvent event) {
if (event.isLegacyPassthrough()) return;
if (event.getType() == ITyped.Type.PRE) {
event.getChannel().pipeline().addLast("eaglercraft-initial-handler", new EaglercraftInitialHandler());
}
if (event.getType() == ITyped.Type.POST) {
if (event.getType() == ITyped.Type.POST && FunnyConfig.eaglerUtils) {
event.getChannel().pipeline().addAfter("eaglercraft-initial-handler", "ayun-eag-detector", new EaglerConnectionHandler());
}
}

View File

@ -0,0 +1,13 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
public class MsgPromise {
public Object msg;
public ChannelPromise promise;
public MsgPromise(Object msg, ChannelPromise promise) {
this.msg = msg;
this.promise = promise;
}
}

View File

@ -0,0 +1,233 @@
package me.ayunami2000.ayunViaProxyEagUtils;
public class SHA256Digest extends GeneralDigest {
private static final int DIGEST_LENGTH = 32;
private int H1, H2, H3, H4, H5, H6, H7, H8;
private int[] X = new int[64];
private int xOff;
public SHA256Digest() {
reset();
}
public static int bigEndianToInt(byte[] bs, int off) {
int n = bs[off] << 24;
n |= (bs[++off] & 0xff) << 16;
n |= (bs[++off] & 0xff) << 8;
n |= (bs[++off] & 0xff);
return n;
}
public static void bigEndianToInt(byte[] bs, int off, int[] ns) {
for (int i = 0; i < ns.length; ++i) {
ns[i] = bigEndianToInt(bs, off);
off += 4;
}
}
public static byte[] intToBigEndian(int n) {
byte[] bs = new byte[4];
intToBigEndian(n, bs, 0);
return bs;
}
public static void intToBigEndian(int n, byte[] bs, int off) {
bs[off] = (byte) (n >>> 24);
bs[++off] = (byte) (n >>> 16);
bs[++off] = (byte) (n >>> 8);
bs[++off] = (byte) (n);
}
protected void processWord(byte[] in, int inOff) {
X[xOff] = bigEndianToInt(in, inOff);
if (++xOff == 16) {
processBlock();
}
}
protected void processLength(long bitLength) {
if (xOff > 14) {
processBlock();
}
X[14] = (int) (bitLength >>> 32);
X[15] = (int) (bitLength & 0xffffffff);
}
public int doFinal(byte[] out, int outOff) {
finish();
intToBigEndian(H1, out, outOff);
intToBigEndian(H2, out, outOff + 4);
intToBigEndian(H3, out, outOff + 8);
intToBigEndian(H4, out, outOff + 12);
intToBigEndian(H5, out, outOff + 16);
intToBigEndian(H6, out, outOff + 20);
intToBigEndian(H7, out, outOff + 24);
intToBigEndian(H8, out, outOff + 28);
reset();
return DIGEST_LENGTH;
}
/**
* reset the chaining variables
*/
public void reset() {
super.reset();
/*
* SHA-256 initial hash value The first 32 bits of the fractional parts of the
* square roots of the first eight prime numbers
*/
H1 = 0x6a09e667;
H2 = 0xbb67ae85;
H3 = 0x3c6ef372;
H4 = 0xa54ff53a;
H5 = 0x510e527f;
H6 = 0x9b05688c;
H7 = 0x1f83d9ab;
H8 = 0x5be0cd19;
xOff = 0;
for (int i = 0; i != X.length; i++) {
X[i] = 0;
}
}
protected void processBlock() {
//
// expand 16 word block into 64 word blocks.
//
for (int t = 16; t <= 63; t++) {
X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
}
//
// set up working variables.
//
int a = H1;
int b = H2;
int c = H3;
int d = H4;
int e = H5;
int f = H6;
int g = H7;
int h = H8;
int t = 0;
for (int i = 0; i < 8; i++) {
// t = 8 * i
h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
d += h;
h += Sum0(a) + Maj(a, b, c);
++t;
// t = 8 * i + 1
g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
c += g;
g += Sum0(h) + Maj(h, a, b);
++t;
// t = 8 * i + 2
f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
b += f;
f += Sum0(g) + Maj(g, h, a);
++t;
// t = 8 * i + 3
e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
a += e;
e += Sum0(f) + Maj(f, g, h);
++t;
// t = 8 * i + 4
d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
h += d;
d += Sum0(e) + Maj(e, f, g);
++t;
// t = 8 * i + 5
c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
g += c;
c += Sum0(d) + Maj(d, e, f);
++t;
// t = 8 * i + 6
b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
f += b;
b += Sum0(c) + Maj(c, d, e);
++t;
// t = 8 * i + 7
a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
e += a;
a += Sum0(b) + Maj(b, c, d);
++t;
}
H1 += a;
H2 += b;
H3 += c;
H4 += d;
H5 += e;
H6 += f;
H7 += g;
H8 += h;
//
// reset the offset and clean out the word buffer.
//
xOff = 0;
for (int i = 0; i < 16; i++) {
X[i] = 0;
}
}
/* SHA-256 functions */
private static int Ch(int x, int y, int z) {
return (x & y) ^ ((~x) & z);
// return z ^ (x & (y ^ z));
}
private static int Maj(int x, int y, int z) {
// return (x & y) ^ (x & z) ^ (y & z);
return (x & y) | (z & (x ^ y));
}
private static int Sum0(int x) {
return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10));
}
private static int Sum1(int x) {
return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7));
}
private static int Theta0(int x) {
return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
}
private static int Theta1(int x) {
return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10);
}
/*
* SHA-256 Constants (represent the first 32 bits of the fractional parts of the
* cube roots of the first sixty-four prime numbers)
*/
static final int K[] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138,
0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70,
0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa,
0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
}

View File

@ -21,7 +21,7 @@ public class SkinPackets {
break;
}
case 6: {
if (EaglerXSkinHandler.skinService.loadPremiumSkins) {
if (FunnyConfig.premiumSkins) {
processGetOtherSkinByURL(data, sender, skinService);
}
break;

View File

@ -6,15 +6,9 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import net.raphimc.netminecraft.constants.MCPackets;
import net.raphimc.netminecraft.packet.PacketTypes;
import net.raphimc.vialegacy.ViaLegacy;
import net.raphimc.vialegacy.ViaLegacyConfig;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.cli.options.Options;
import javax.imageio.ImageIO;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@ -23,15 +17,10 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class SkinService {
public final boolean loadPremiumSkins;
private final ConcurrentHashMap<UUID, CachedSkin> skinCache;
public SkinService() {
this.skinCache = new ConcurrentHashMap<>();
File funnyFile = new File("ViaLoader", "eaglerskins.yml");
FunnyConfig funnyConfig = new FunnyConfig(funnyFile);
funnyConfig.reloadConfig();
this.loadPremiumSkins = funnyConfig.getPremiumSkins();
}
private static void sendData(final ChannelHandlerContext ctx, final byte[] data) {

View File

@ -0,0 +1,61 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.ssl.SslCompletionEvent;
import java.util.ArrayList;
import java.util.List;
public class WebSocketConnectedNotifier extends ChannelDuplexHandler {
private final List<Object> msgsRead = new ArrayList<>();
private final List<MsgPromise> msgsWrite = new ArrayList<>();
@Override
public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) {
if (evt == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
ctx.fireChannelActive();
ctx.fireUserEventTriggered(evt);
ctx.pipeline().remove(this);
for (final Object msg : msgsRead)
ctx.fireChannelRead(msg);
msgsRead.clear();
for (final MsgPromise msg : msgsWrite)
ctx.write(msg.msg, msg.promise);
ctx.flush();
msgsWrite.clear();
} else if (evt == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_TIMEOUT) {
ctx.fireUserEventTriggered(evt);
ctx.close();
} else {
if (evt instanceof SslCompletionEvent && !((SslCompletionEvent) evt).isSuccess()) {
((SslCompletionEvent) evt).cause().printStackTrace();
}
ctx.fireUserEventTriggered(evt);
}
}
@Override
public void channelActive(final ChannelHandlerContext ctx) {
//
}
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
if (msg instanceof ByteBuf) {
msgsRead.add(msg);
} else {
ctx.fireChannelRead(msg);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof BinaryWebSocketFrame || msg instanceof ByteBuf) {
msgsWrite.add(new MsgPromise(msg, promise));
} else {
ctx.write(msg, promise);
}
}
}

View File

@ -1,2 +0,0 @@
# Use premium skins
premium-skins: false

View File

@ -0,0 +1,8 @@
# Use premium skins
premium-skins: false
# Handle Eagler skins and voice on the proxy side
eagler-utils: true
# Sync Eagler skins
eagler-skins: true
# Enable Eagler voice chat
eagler-voice: true