Fix utils and create MTProtoPlainSender
This commit is contained in:
parent
11755fb266
commit
80ef876d92
|
@ -1,14 +1,23 @@
|
||||||
package io.github.lonamiwebs.overgram;
|
package io.github.lonamiwebs.overgram;
|
||||||
|
|
||||||
|
import io.github.lonamiwebs.overgram.crypto.Authenticator;
|
||||||
|
import io.github.lonamiwebs.overgram.network.MTProtoPlainSender;
|
||||||
import io.github.lonamiwebs.overgram.network.connection.Connection;
|
import io.github.lonamiwebs.overgram.network.connection.Connection;
|
||||||
import io.github.lonamiwebs.overgram.network.connection.TcpFull;
|
import io.github.lonamiwebs.overgram.network.connection.TcpFull;
|
||||||
|
import io.github.lonamiwebs.overgram.utils.BinaryWriter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class Overgram {
|
public class Overgram {
|
||||||
public static void main(final String... args) throws IOException {
|
public static void main(final String... args) throws IOException, ClassNotFoundException {
|
||||||
final Connection connection = new TcpFull();
|
final Connection connection = new TcpFull();
|
||||||
|
try {
|
||||||
connection.connect("149.154.167.91", 443);
|
connection.connect("149.154.167.91", 443);
|
||||||
|
|
||||||
|
final MTProtoPlainSender sender = new MTProtoPlainSender(connection);
|
||||||
|
Authenticator.doAuthentication(sender);
|
||||||
|
} finally {
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.github.lonamiwebs.overgram.crypto;
|
||||||
|
|
||||||
|
import io.github.lonamiwebs.overgram.network.MTProtoPlainSender;
|
||||||
|
import io.github.lonamiwebs.overgram.tl.Functions;
|
||||||
|
import io.github.lonamiwebs.overgram.tl.TLObject;
|
||||||
|
import io.github.lonamiwebs.overgram.tl.Types;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
public class Authenticator {
|
||||||
|
|
||||||
|
public static void doAuthentication(final MTProtoPlainSender sender) throws IOException, ClassNotFoundException {
|
||||||
|
final byte[] nonceBytes = new byte[16];
|
||||||
|
new SecureRandom().nextBytes(nonceBytes);
|
||||||
|
final BigInteger nonce = new BigInteger(nonceBytes);
|
||||||
|
|
||||||
|
final TLObject result = sender.send(new Functions.ReqPqMulti().nonce(nonce));
|
||||||
|
assert result instanceof Types.ResPQ;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package io.github.lonamiwebs.overgram.network;
|
||||||
|
|
||||||
|
import io.github.lonamiwebs.overgram.network.connection.Connection;
|
||||||
|
import io.github.lonamiwebs.overgram.tl.TLObject;
|
||||||
|
import io.github.lonamiwebs.overgram.tl.TLRequest;
|
||||||
|
import io.github.lonamiwebs.overgram.utils.BinaryReader;
|
||||||
|
import io.github.lonamiwebs.overgram.utils.BinaryWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
public class MTProtoPlainSender {
|
||||||
|
private final Connection connection;
|
||||||
|
private final MTProtoState state;
|
||||||
|
|
||||||
|
public MTProtoPlainSender(final Connection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.state = new MTProtoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TLObject send(final TLRequest request) throws IOException, ClassNotFoundException {
|
||||||
|
final long msgId = state.getNewMsgId();
|
||||||
|
|
||||||
|
final BinaryWriter writer = new BinaryWriter();
|
||||||
|
request.serialize(writer);
|
||||||
|
final byte[] requestBytes = writer.toBytes();
|
||||||
|
writer.clear();
|
||||||
|
|
||||||
|
writer.write(0L);
|
||||||
|
writer.write(msgId);
|
||||||
|
writer.write(requestBytes.length);
|
||||||
|
writer.writeRaw(requestBytes);
|
||||||
|
connection.send(writer.toBytes());
|
||||||
|
|
||||||
|
final byte[] body = connection.recv();
|
||||||
|
if (body[0] == (byte) 0x6c
|
||||||
|
&& body[1] == (byte) 0xfe
|
||||||
|
&& body[2] == (byte) 0xff
|
||||||
|
&& body[3] == (byte) 0xff) {
|
||||||
|
// -404 as little endian, broken authorization
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final BinaryReader reader = new BinaryReader(ByteBuffer.wrap(body).order(ByteOrder.LITTLE_ENDIAN));
|
||||||
|
final long authKeyId = reader.readLong();
|
||||||
|
assert authKeyId == 0;
|
||||||
|
|
||||||
|
final long serverMsgId = reader.readLong();
|
||||||
|
assert serverMsgId != 0;
|
||||||
|
|
||||||
|
final int length = reader.readInt();
|
||||||
|
assert length > 0;
|
||||||
|
|
||||||
|
return reader.readTl();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package io.github.lonamiwebs.overgram.network;
|
||||||
|
|
||||||
|
import io.github.lonamiwebs.overgram.tl.TLObject;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
public class MTProtoState {
|
||||||
|
|
||||||
|
private final long id;
|
||||||
|
private long timeOffset;
|
||||||
|
private int sequence;
|
||||||
|
private long lastMsgId;
|
||||||
|
|
||||||
|
public MTProtoState() {
|
||||||
|
id = new SecureRandom().nextLong();
|
||||||
|
timeOffset = 0;
|
||||||
|
sequence = 0;
|
||||||
|
lastMsgId = 0;
|
||||||
|
|
||||||
|
// TODO auth_key, salt
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createMessage(final TLObject object, final long afterId) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object calcKey(final Object authKey, final byte[] msgKey, final boolean client) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] packMessage(final Object message) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unpackMessage(final byte[] body) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getNewMsgId() {
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
long newMsgId = (((now / 1000) + timeOffset) << 32) | ((now % 1000) << 2);
|
||||||
|
if (lastMsgId >= newMsgId) {
|
||||||
|
newMsgId = lastMsgId + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMsgId = newMsgId;
|
||||||
|
return newMsgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTimeOffset(long correctMsgId) {
|
||||||
|
final long now = System.currentTimeMillis() / 1000L;
|
||||||
|
final long correct = correctMsgId >> 32;
|
||||||
|
timeOffset = correct - now;
|
||||||
|
lastMsgId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSeqNo(final boolean contentRelated) {
|
||||||
|
if (contentRelated) {
|
||||||
|
return 1 + 2 * sequence++;
|
||||||
|
} else {
|
||||||
|
return 2 * sequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,8 +31,7 @@ public class TcpFull extends Connection {
|
||||||
public void send(final byte[] data) throws IOException {
|
public void send(final byte[] data) throws IOException {
|
||||||
final int length = data.length + 12;
|
final int length = data.length + 12;
|
||||||
|
|
||||||
final ByteBuffer buffer = ByteBuffer.allocate(length);
|
final ByteBuffer buffer = ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
|
|
||||||
buffer.putInt(length);
|
buffer.putInt(length);
|
||||||
buffer.putInt(counter);
|
buffer.putInt(counter);
|
||||||
|
@ -48,7 +47,7 @@ public class TcpFull extends Connection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] recv() throws IOException {
|
public byte[] recv() throws IOException {
|
||||||
final ByteBuffer buffer = ByteBuffer.wrap(client.read(8));
|
final ByteBuffer buffer = ByteBuffer.wrap(client.read(8)).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
final int length = buffer.getInt();
|
final int length = buffer.getInt();
|
||||||
final int seq = buffer.getInt();
|
final int seq = buffer.getInt();
|
||||||
final byte[] body = client.read(length - 12);
|
final byte[] body = client.read(length - 12);
|
||||||
|
|
|
@ -6,5 +6,5 @@ import io.github.lonamiwebs.overgram.utils.BinaryWriter;
|
||||||
public abstract class TLObject {
|
public abstract class TLObject {
|
||||||
public abstract void serialize(final BinaryWriter writer);
|
public abstract void serialize(final BinaryWriter writer);
|
||||||
|
|
||||||
public abstract void deserialize(final BinaryReader reader);
|
public abstract void deserialize(final BinaryReader reader) throws ClassNotFoundException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package io.github.lonamiwebs.overgram.utils;
|
package io.github.lonamiwebs.overgram.utils;
|
||||||
|
|
||||||
|
import io.github.lonamiwebs.overgram.tl.TLObject;
|
||||||
|
import io.github.lonamiwebs.overgram.tl.Types;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -40,6 +44,18 @@ public class BinaryReader {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigInteger readInt128() {
|
||||||
|
final byte[] bytes = new byte[16];
|
||||||
|
buffer.get(bytes);
|
||||||
|
return new BigInteger(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger readInt256() {
|
||||||
|
final byte[] bytes = new byte[32];
|
||||||
|
buffer.get(bytes);
|
||||||
|
return new BigInteger(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
public double readDouble() {
|
public double readDouble() {
|
||||||
return buffer.getDouble();
|
return buffer.getDouble();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +79,9 @@ public class BinaryReader {
|
||||||
|
|
||||||
final byte[] data = new byte[length];
|
final byte[] data = new byte[length];
|
||||||
buffer.get(data);
|
buffer.get(data);
|
||||||
buffer.position(buffer.position() + padding);
|
if (padding > 0) {
|
||||||
|
buffer.position(buffer.position() + 4 - padding);
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,14 +109,16 @@ public class BinaryReader {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object readTl() {
|
public TLObject readTl() throws ClassNotFoundException {
|
||||||
return null;
|
final TLObject object = Types.getFromId(readInt());
|
||||||
|
object.deserialize(this);
|
||||||
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<?> readTlList() {
|
public List<?> readTlList() throws ClassNotFoundException {
|
||||||
buffer.position(buffer.position() + 4); // vector code
|
buffer.position(buffer.position() + 4); // vector code
|
||||||
final int size = buffer.getInt();
|
final int size = buffer.getInt();
|
||||||
final List<Object> result = new ArrayList<>(size);
|
final List<TLObject> result = new ArrayList<>(size);
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
result.add(readTl());
|
result.add(readTl());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,55 +3,100 @@ package io.github.lonamiwebs.overgram.utils;
|
||||||
|
|
||||||
import io.github.lonamiwebs.overgram.tl.TLObject;
|
import io.github.lonamiwebs.overgram.tl.TLObject;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BinaryWriter {
|
public class BinaryWriter {
|
||||||
private final ByteBuffer buffer;
|
|
||||||
|
private static final ByteOrder ORDER = ByteOrder.LITTLE_ENDIAN;
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream buffer;
|
||||||
|
|
||||||
public BinaryWriter() {
|
public BinaryWriter() {
|
||||||
buffer = ByteBuffer.allocate(128);
|
buffer = new ByteArrayOutputStream(128);
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final int value) {
|
public void write(final int value) {
|
||||||
buffer.putInt(value);
|
try {
|
||||||
|
buffer.write(ByteBuffer.allocate(4).order(ORDER).putInt(value).array());
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final long value) {
|
public void write(final long value) {
|
||||||
buffer.putLong(value);
|
try {
|
||||||
|
buffer.write(ByteBuffer.allocate(8).order(ORDER).putLong(value).array());
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(final BigInteger value) {
|
||||||
|
final byte[] bytes = value.toByteArray();
|
||||||
|
|
||||||
|
// It's really unlike an int256 has only 16 bytes so we're good
|
||||||
|
final int size = bytes.length > 16 ? 32 : 16;
|
||||||
|
if (ORDER == ByteOrder.BIG_ENDIAN) {
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
buffer.write(i < bytes.length ? bytes[i] : 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = size; i-- != 0; ) {
|
||||||
|
buffer.write(i < bytes.length ? bytes[i] : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final double value) {
|
public void write(final double value) {
|
||||||
buffer.putDouble(value);
|
try {
|
||||||
|
buffer.write(ByteBuffer.allocate(8).order(ORDER).putDouble(value).array());
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final boolean value) {
|
public void write(final boolean value) {
|
||||||
buffer.putInt(value ? 0x997275b5 : 0xbc799737);
|
write(value ? 0x997275b5 : 0xbc799737);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final byte[] bytes) {
|
public void write(final byte[] bytes) {
|
||||||
|
try {
|
||||||
int padding;
|
int padding;
|
||||||
if (bytes.length < 0xfe) {
|
if (bytes.length < 0xfe) {
|
||||||
padding = (bytes.length + 1) % 4;
|
padding = (bytes.length + 1) % 4;
|
||||||
buffer.put((byte) bytes.length);
|
buffer.write(bytes.length);
|
||||||
buffer.put(bytes);
|
buffer.write(bytes);
|
||||||
} else {
|
} else {
|
||||||
padding = bytes.length % 4;
|
padding = bytes.length % 4;
|
||||||
buffer.putInt(bytes.length << 8 | 0xfe);
|
write(bytes.length << 8 | 0xfe);
|
||||||
buffer.put(bytes);
|
buffer.write(bytes);
|
||||||
}
|
}
|
||||||
if (padding != 0) {
|
if (padding != 0) {
|
||||||
for (padding = 4 - padding; padding-- != 0; ) {
|
for (padding = 4 - padding; padding-- != 0; ) {
|
||||||
buffer.put((byte) 0);
|
buffer.write(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeRaw(final byte[] bytes) {
|
||||||
|
try {
|
||||||
|
buffer.write(bytes);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final String string) {
|
public void write(final String string) {
|
||||||
buffer.put(StandardCharsets.UTF_8.encode(string));
|
write(StandardCharsets.UTF_8.encode(string).array());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Handle boxed vs unboxed types (and vector<>)
|
// TODO Handle boxed vs unboxed types (and vector<>)
|
||||||
|
@ -88,4 +133,21 @@ public class BinaryWriter {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
buffer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
return buffer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Arrays.toString(toBytes());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue