diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/crypto/Authenticator.java b/lib/src/main/java/io/github/lonamiwebs/overgram/crypto/Authenticator.java index c45b680..a450453 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/crypto/Authenticator.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/crypto/Authenticator.java @@ -1,6 +1,7 @@ package io.github.lonamiwebs.overgram.crypto; import io.github.lonamiwebs.overgram.network.MTProtoPlainSender; +import io.github.lonamiwebs.overgram.network.MTProtoState; import io.github.lonamiwebs.overgram.tl.Abstract; import io.github.lonamiwebs.overgram.tl.Functions; import io.github.lonamiwebs.overgram.tl.Types; @@ -14,7 +15,8 @@ import java.nio.ByteBuffer; public class Authenticator { - public static AuthKey doAuthentication(final MTProtoPlainSender sender) throws IOException, ClassNotFoundException { + public static void doAuthentication(final MTProtoPlainSender sender, final MTProtoState state) + throws IOException, ClassNotFoundException { final BigInteger nonce = new BigInteger(Utils.randomBytes(16)); final Types.ResPQ resPq = sender.send( new Functions.ReqPqMulti().nonce(nonce)); @@ -55,6 +57,7 @@ public class Authenticator { throw new SecurityException("Step 2 could not find a known RSA key"); } + // TODO This step fails half the time, why? final Abstract.ServerDHParams abstractDhParams = sender.send( new Functions.ReqDHParams() .nonce(resPq.nonce()) @@ -149,6 +152,7 @@ public class Authenticator { throw new SecurityException("Step 3 DH gen data have invalid nonce hash"); } - return authKey; // TODO And time offset + state.authKey = authKey; + state.setTimeOffset(timeOffset); } } diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoSender.java b/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoSender.java index 6ba9cdf..7416f05 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoSender.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoSender.java @@ -67,7 +67,7 @@ public class MTProtoSender { success = false; for (int i = 0; i < retries; ++i) { try { - state.authKey = Authenticator.doAuthentication(plain); + Authenticator.doAuthentication(plain, state); success = true; break; } catch (SecurityException | IOException | ClassNotFoundException ignored) { @@ -222,6 +222,7 @@ public class MTProtoSender { } try { processMessage(message); + } catch (ClassNotFoundException ignored) { } catch (InterruptedException ignored) { doDisconnect(); return; @@ -229,7 +230,7 @@ public class MTProtoSender { } } - private void processMessage(final TLMessage message) throws InterruptedException { + private void processMessage(final TLMessage message) throws InterruptedException, ClassNotFoundException { pendingAck.add(message.id); if (message.object instanceof RPCResult) { handleRpcResult(message); @@ -264,33 +265,37 @@ public class MTProtoSender { } } - private void handleRpcResult(final TLMessage message) { + private void handleRpcResult(final TLMessage message) throws ClassNotFoundException { final RPCResult result = (RPCResult) message.object; final TLMessage replyMessage = pendingMessages.remove(result.reqMsgId); - if (result.error != null) { - replyMessage.future.completeExceptionally(new RPCError(result.error)); - return; - } - - final BinaryReader reader = new BinaryReader(ByteBuffer.wrap(result.result)); + boolean ok = false; try { - replyMessage.future.complete(((TLRequest) replyMessage.object).readResult(reader)); - } catch (ClassNotFoundException e) { - replyMessage.future.completeExceptionally(e); + if (result.error != null) { + replyMessage.future.completeExceptionally(new RPCError(result.error)); + } else { + final BinaryReader reader = new BinaryReader(ByteBuffer.wrap(result.result)); + replyMessage.future.complete(((TLRequest) replyMessage.object).readResult(reader)); + } + ok = true; + } finally { + if (!ok) { + replyMessage.future.completeExceptionally(new Exception()); + } } } - public void handleContainer(final TLMessage message) throws InterruptedException { + public void handleContainer(final TLMessage message) throws InterruptedException, ClassNotFoundException { final MessageContainer result = (MessageContainer) message.object; for (final TLMessage innerMessage : result.messages) { processMessage(innerMessage); } } - public void handleGzipPacked(final TLMessage message) throws InterruptedException { + public void handleGzipPacked(final TLMessage message) throws InterruptedException, ClassNotFoundException { final GzipPacked result = (GzipPacked) message.object; - message.object = result.packedObject(); + final BinaryReader reader = new BinaryReader(ByteBuffer.wrap(result.unpackedData)); + message.object = reader.readTl(); processMessage(message); } @@ -323,9 +328,11 @@ public class MTProtoSender { final TLMessage badMessage = pendingMessages.get(result.badMsgId()); if (result.errorCode() == 16 || result.errorCode() == 17) { + state.updateTimeOffset(message.id); if (badMessage != null) { - // resend and update time offset - throw new UnsupportedOperationException(); + pendingMessages.remove(badMessage.id); + badMessage.id = state.getNewMsgId(); + pendingMessages.put(badMessage.id, badMessage); } return; } diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoState.java b/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoState.java index 5695031..e095e6a 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoState.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/network/MTProtoState.java @@ -114,12 +114,9 @@ public class MTProtoState { throw new SecurityException("Server replied with a wrong session ID"); } - final long remoteMsgId = tlReader.readLong(); - final int remoteSeq = tlReader.readInt(); - tlReader.readInt(); // inner message length - - final TLObject object = tlReader.readTl(); - return new TLMessage(remoteMsgId, remoteSeq, object); + final TLMessage message = new TLMessage(); + message.deserialize(tlReader); + return message; } public long getNewMsgId() { @@ -140,6 +137,10 @@ public class MTProtoState { lastMsgId = 0; } + public void setTimeOffset(final long timeOffset) { + this.timeOffset = timeOffset; + } + public int getSeqNo(final boolean contentRelated) { if (contentRelated) { return 1 + 2 * sequence++; diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/network/connection/TcpFull.java b/lib/src/main/java/io/github/lonamiwebs/overgram/network/connection/TcpFull.java index 0ca40cd..4e1eefa 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/network/connection/TcpFull.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/network/connection/TcpFull.java @@ -51,8 +51,16 @@ public class TcpFull extends Connection { final int length = buffer.getInt(); final int seq = buffer.getInt(); final byte[] body = client.read(length - 12); - final int crc = ByteBuffer.wrap(client.read(4)).getInt(); - // TODO Check crc + final int crc = ByteBuffer.wrap(client.read(4)).order(ByteOrder.LITTLE_ENDIAN).getInt(); + + final CRC32 validCrc = new CRC32(); + buffer.rewind(); + validCrc.update(buffer.array()); + validCrc.update(body); + if ((int) validCrc.getValue() != crc) { + throw new SecurityException("Invalid CRC"); + } + return body; } } diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/GzipPacked.java b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/GzipPacked.java index c4b15b6..b8fba3c 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/GzipPacked.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/GzipPacked.java @@ -3,24 +3,53 @@ package io.github.lonamiwebs.overgram.tl; import io.github.lonamiwebs.overgram.utils.BinaryReader; import io.github.lonamiwebs.overgram.utils.BinaryWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + public class GzipPacked extends TLObject { public static final int CONSTRUCTOR_ID = 812830625; + public byte[] unpackedData; + @Override public void serialize(final BinaryWriter writer) { - throw new UnsupportedOperationException(); + try { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final GZIPOutputStream gzip = new GZIPOutputStream(out); + gzip.write(unpackedData); + gzip.finish(); + writer.write(out.toByteArray()); + } catch (IOException ignored) { + throw new RuntimeException(); + } } @Override public void deserialize(final BinaryReader reader) throws ClassNotFoundException { - throw new UnsupportedOperationException(); - } - - public TLObject packedObject() { - throw new UnsupportedOperationException(); + try { + final GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(reader.read())); + final ByteArrayOutputStream out = new ByteArrayOutputStream(4096); + final byte[] buffer = new byte[4096]; + int read; + while ((read = gzip.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + unpackedData = out.toByteArray(); + } catch (IOException ignored) { + throw new RuntimeException(); + } } public static byte[] unzip(final BinaryReader reader) { - throw new UnsupportedOperationException(); + final GzipPacked gzipPacked = new GzipPacked(); + try { + gzipPacked.deserialize(reader); + } catch (ClassNotFoundException ignored) { + throw new RuntimeException(); + } + return gzipPacked.unpackedData; } } diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/MessageContainer.java b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/MessageContainer.java index 8da82aa..fef8039 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/MessageContainer.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/MessageContainer.java @@ -13,7 +13,11 @@ public class MessageContainer extends TLObject { @Override public void serialize(final BinaryWriter writer) { - throw new UnsupportedOperationException(); + writer.write(CONSTRUCTOR_ID); + writer.write(messages.size()); + for (final TLMessage message : messages) { + writer.write(message); + } } @Override @@ -21,13 +25,9 @@ public class MessageContainer extends TLObject { messages.clear(); final int count = reader.readInt(); for (int i = 0; i < count; ++i) { - final long msgId = reader.readLong(); - final int seqNo = reader.readInt(); - final int length = reader.readInt(); - final int before = reader.tell(); - final TLObject object = reader.readTl(); - reader.set(before + length); - messages.add(new TLMessage(msgId, seqNo, object)); + final TLMessage message = new TLMessage(); + message.deserialize(reader); + messages.add(message); } } } diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/RPCResult.java b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/RPCResult.java index 2068eab..c81fd56 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/RPCResult.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/RPCResult.java @@ -3,8 +3,6 @@ package io.github.lonamiwebs.overgram.tl; import io.github.lonamiwebs.overgram.utils.BinaryReader; import io.github.lonamiwebs.overgram.utils.BinaryWriter; -import java.util.Arrays; - public class RPCResult extends TLObject { public static final int CONSTRUCTOR_ID = -212046591; diff --git a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/TLMessage.java b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/TLMessage.java index 4410f74..5a1b37f 100644 --- a/lib/src/main/java/io/github/lonamiwebs/overgram/tl/TLMessage.java +++ b/lib/src/main/java/io/github/lonamiwebs/overgram/tl/TLMessage.java @@ -12,6 +12,9 @@ public class TLMessage extends TLObject { public int seqNo; public TLObject object; + public TLMessage() { + } + public TLMessage(final long id, final int seqNo, final TLObject object) { this.id = id; this.seqNo = seqNo; @@ -20,7 +23,7 @@ public class TLMessage extends TLObject { } @Override - public void serialize(BinaryWriter writer) { + public void serialize(final BinaryWriter writer) { final BinaryWriter tmp = new BinaryWriter(); tmp.write(object); writer.write(id); @@ -30,7 +33,12 @@ public class TLMessage extends TLObject { } @Override - public void deserialize(BinaryReader reader) throws ClassNotFoundException { - throw new UnsupportedOperationException(); + public void deserialize(final BinaryReader reader) throws ClassNotFoundException { + id = reader.readLong(); + seqNo = reader.readInt(); + final int length = reader.readInt(); + final int before = reader.tell(); + object = reader.readTl(); + reader.set(before + length); } }