Fix some pylint concerns

This commit is contained in:
Ammar Askar 2015-04-03 01:44:03 +05:00
parent f7ed4da0d0
commit dcb2845ac4
4 changed files with 86 additions and 71 deletions

View File

@ -5,19 +5,17 @@ import threading
import socket
import time
import select
import authentication
from packets import *
import packets
from types import VarInt
PROTOCOL_VERSION = 47
from minecraft import PROTOCOL_VERSION
class ConnectionOptions(object):
# TODO: allow these options to be overriden from a constructor below
address = None
port = None
use_encryption = True
compression_threshold = -1
compression_enabled = False
@ -40,8 +38,11 @@ class Connection(object):
spawned = False
def __init__(self, address, port, auth_token):
"""Sets up an instance of this object to be able to connect to a minecraft server.
The connect method needs to be called in order to actually begin the connection
"""Sets up an instance of this object to be able to connect to a
minecraft server.
The connect method needs to be called in order to actually begin
the connection
:param address: address of the server to connect to
:param port(int): port of the server to connect to
@ -58,10 +59,12 @@ class Connection(object):
def write_packet(self, packet, force=False):
"""Writes a packet to the server.
If force is set to true, the method attempts to acquire the write lock and write the packet
out immediately, and as such may block.
If force is false then the packet will be added to the end of the packet writing queue
to be sent 'as soon as possible'
If force is set to true, the method attempts to acquire the write lock
and write the packet out immediately, and as such may block.
If force is false then the packet will be added to the end of the
packet writing queue to be sent 'as soon as possible'
:param packet: The :class:`network.packets.Packet` to write
:param force(bool): Specifies if the packet write should be immediate
@ -84,14 +87,17 @@ class Connection(object):
:param method: The method which will be called back with the packet
:param args: The packets to listen for
"""
self.packet_listeners.append(PacketListener(method, *args))
self.packet_listeners.append(packets.PacketListener(method, *args))
def _pop_packet(self):
# Pops the topmost packet off the outgoing queue and writes it out through the socket
# Pops the topmost packet off the outgoing queue and writes it out
# through the socket
#
# Mostly an internal convenience function, caller should make sure they have the
# write lock acquired to avoid issues caused by asynchronous access to the socket.
# This should be the only method that removes elements from the outbound queue
# Mostly an internal convenience function, caller should make sure
# they have the write lock acquired to avoid issues caused by
# asynchronous access to the socket.
# This should be the only method that removes elements from the
# outbound queue
if len(self._outgoing_packet_queue) == 0:
return False
else:
@ -108,7 +114,7 @@ class Connection(object):
self._start_network_thread()
self.reactor = StatusReactor(self)
request_packet = RequestPacket()
request_packet = packets.RequestPacket()
self.write_packet(request_packet)
def connect(self):
@ -119,21 +125,23 @@ class Connection(object):
self.reactor = LoginReactor(self)
self._start_network_thread()
login_start_packet = LoginStartPacket()
login_start_packet = packets.LoginStartPacket()
login_start_packet.name = self.auth_token.profile.name
self.write_packet(login_start_packet)
def _connect(self):
# Connect a socket to the server and create a file object from the socket
# The file object is used to read any and all data from the socket since it's "guaranteed"
# to read the number of bytes specified, the socket itself will mostly be
# used to write data upstream to the server
# Connect a socket to the server and create a file object from the
# socket.
# The file object is used to read any and all data from the socket
# since it's "guaranteed" to read the number of bytes specified,
# the socket itself will mostly be used to write data upstream to
# the server.
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.options.address, self.options.port))
self.file_object = self.socket.makefile()
def _handshake(self, next_state=2):
handshake = HandShakePacket()
handshake = packets.HandShakePacket()
handshake.protocol_version = PROTOCOL_VERSION
handshake.server_address = self.options.address
handshake.server_port = self.options.port
@ -155,7 +163,8 @@ class NetworkingThread(threading.Thread):
while True:
if self.interrupt:
break
# Attempt to write out as many as 300 packets as possible every 0.05 seconds (20 ticks per second)
# Attempt to write out as many as 300 packets as possible every
# 0.05 seconds (20 ticks per second)
num_packets = 0
self.connection._write_lock.acquire()
while self.connection._pop_packet():
@ -166,7 +175,8 @@ class NetworkingThread(threading.Thread):
# Read and react to as many as 50 packets
num_packets = 0
packet = self.connection.reactor.read_packet(self.connection.file_object)
packet = self.connection.reactor.read_packet(
self.connection.file_object)
while packet:
num_packets += 1
@ -176,7 +186,8 @@ class NetworkingThread(threading.Thread):
if num_packets >= 50:
break
packet = self.connection.reactor.read_packet(self.connection.file_object)
packet = self.connection.reactor.read_packet(
self.connection.file_object)
time.sleep(0.05)
@ -194,23 +205,26 @@ class PacketReactor(object):
self.connection = connection
def read_packet(self, stream):
ready_to_read, _, __ = select.select([self.connection.socket], [], [], self.TIME_OUT)
ready_to_read, _, __ = select.select([self.connection.socket], [], [],
self.TIME_OUT)
if self.connection.socket in ready_to_read:
length = VarInt.read_socket(self.connection.socket)
packet_data = PacketBuffer()
packet_data = packets.PacketBuffer()
packet_data.send(stream.read(length))
# Ensure we read all the packet
while len(packet_data.get_writable()) < length:
packet_data.send(stream.read(length - len(packet_data.get_writable())))
packet_data.send(
stream.read(length - len(packet_data.get_writable())))
packet_data.reset_cursor()
if self.connection.options.compression_enabled:
compressed_size = VarInt.read(packet_data)
if compressed_size > 0:
decompressed_packet = decompress(packet_data.read(compressed_size))
decompressed_packet = decompress(
packet_data.read(compressed_size))
packet_data.reset()
packet_data.send(decompressed_packet)
packet_data.reset_cursor()
@ -224,7 +238,7 @@ class PacketReactor(object):
packet.read(packet_data)
return packet
else:
return Packet()
return packets.Packet()
else:
return None
@ -233,39 +247,39 @@ class PacketReactor(object):
class LoginReactor(PacketReactor):
clientbound_packets = state_login_clientbound
clientbound_packets = packets.STATE_LOGIN_CLIENTBOUND
def react(self, packet):
# TODO: Add some way to bypass encryption? (connection.options.use_encryption) Not sure if it's still possible.
if packet.packet_name == "encryption request":
import encryption
secret = encryption.generate_shared_secret()
encrypted_token, encrypted_secret = encryption.encrypt_token_and_secret(packet.public_key,
packet.verify_token, secret)
token, encrypted_secret = encryption.encrypt_token_and_secret(
packet.public_key, packet.verify_token, secret)
# A server id of '-' means the server is in offline mode
if packet.server_id != '-':
server_id = encryption.generate_verification_hash(packet.server_id, secret, packet.public_key)
self.connection.auth_token.join(server_id)
encryption_response = EncryptionResponsePacket()
encryption_response.shared_secret = encrypted_secret
encryption_response.verify_token = encrypted_token
server_id = encryption.generate_verification_hash(
packet.server_id, secret, packet.public_key)
# Forced because we don't want to send this encrypted which it will be
# if we put it in the queue as we'd have wrapped the socket and file object by then
self.connection.auth_token.join(server_id)
encryption_response = packets.EncryptionResponsePacket()
encryption_response.shared_secret = encrypted_secret
encryption_response.verify_token = token
# Forced because we'll have encrypted the connection by the time
# it reaches the outgoing queue
self.connection.write_packet(encryption_response, force=True)
# Enable the encryption
cipher = encryption.create_AES_cipher(secret)
encryptor = cipher.encryptor()
decryptor = cipher.decryptor()
self.connection.socket = encryption.EncryptedSocketWrapper(self.connection.socket,
encryptor, decryptor)
self.connection.file_object = encryption.EncryptedFileObjectWrapper(self.connection.file_object,
decryptor)
self.connection.socket = encryption.EncryptedSocketWrapper(
self.connection.socket, encryptor, decryptor)
self.connection.file_object = encryption.EncryptedFileObjectWrapper(
self.connection.file_object, decryptor)
if packet.packet_name == "disconnect":
print(packet.json_data) # TODO: handle propagating this back
@ -279,7 +293,7 @@ class LoginReactor(PacketReactor):
class PlayingReactor(PacketReactor):
clientbound_packets = state_playing_clientbound
clientbound_packets = packets.STATE_PLAYING_CLIENTBOUND
def react(self, packet):
if packet.packet_name == "set compression":
@ -287,12 +301,12 @@ class PlayingReactor(PacketReactor):
self.connection.options.compression_enabled = True
if packet.packet_name == "keep alive":
keep_alive_packet = KeepAlivePacket()
keep_alive_packet = packets.KeepAlivePacket()
keep_alive_packet.keep_alive_id = packet.keep_alive_id
self.connection.write_packet(keep_alive_packet)
if packet.packet_name == "player position and look":
position_response = PositionAndLookPacket()
position_response = packets.PositionAndLookPacket()
position_response.x = packet.x
position_response.feet_y = packet.y
position_response.z = packet.z
@ -308,15 +322,15 @@ class PlayingReactor(PacketReactor):
class StatusReactor(PacketReactor):
clientbound_packets = state_status_clientbound
clientbound_packets = packets.STATE_STATUS_CLIENTBOUND
def react(self, packet):
if packet.id == ResponsePacket.id:
if packet.id == packets.ResponsePacket.id:
import json
print json.loads(packet.json_response)
ping_packet = PingPacket()
ping_packet = packets.PingPacket()
ping_packet.time = int(time.time())
self.connection.write_packet(ping_packet)

View File

@ -12,12 +12,14 @@ def generate_shared_secret():
def create_AES_cipher(shared_secret):
cipher = Cipher(algorithms.AES(shared_secret), modes.CFB8(shared_secret), backend=default_backend())
cipher = Cipher(algorithms.AES(shared_secret), modes.CFB8(shared_secret),
backend=default_backend())
return cipher
def encrypt_token_and_secret(pubkey, verification_token, shared_secret):
"""Encrypts the verification token and shared secret with the server's public key
"""Encrypts the verification token and shared secret
with the server's public key.
:param pubkey: The RSA public key provided by the server
:param verification_token: The verification token provided by the server
@ -45,8 +47,8 @@ def generate_verification_hash(server_id, shared_secret, public_key):
def minecraft_sha1_hash_digest(sha1_hash):
# Minecraft first parses the sha1 bytes as a signed number and then spits outs
# its hex representation
# Minecraft first parses the sha1 bytes as a signed number and then
# spits outs its hex representation
number_representation = _number_from_bytes(sha1_hash.digest(), signed=True)
return format(number_representation, 'x')
@ -82,4 +84,4 @@ class EncryptedSocketWrapper(object):
self.actual_socket.send(self.encryptor.update(data))
def fileno(self):
return self.actual_socket.fileno()
return self.actual_socket.fileno()

View File

@ -102,10 +102,10 @@ class HandShakePacket(Packet):
{'next_state': VarInt}]
state_handshake_clientbound = {
STATE_HANDSHAKE_CLIENTBOUND = {
}
state_handshake_serverbound = {
STATE_HANDSHAKE_SERVERBOUND = {
0x00: HandShakePacket
}
@ -125,7 +125,7 @@ class PingPacket(Packet):
definition = [
{'time': Long}]
state_status_clientbound = {
STATE_STATUS_CLIENTBOUND = {
0x00: ResponsePacket,
0x01: PingPacket
}
@ -143,7 +143,7 @@ class PingPacket(Packet):
definition = [
{'time': Long}]
state_status_serverbound = {
STATE_STATUS_SERVERBOUND = {
0x00: RequestPacket,
0x01: PingPacket
}
@ -181,7 +181,7 @@ class SetCompressionPacket(Packet):
definition = [
{'threshold': VarInt}]
state_login_clientbound = {
STATE_LOGIN_CLIENTBOUND = {
0x00: DisconnectPacket,
0x01: EncryptionRequestPacket,
0x02: LoginSuccessPacket,
@ -203,7 +203,7 @@ class EncryptionResponsePacket(Packet):
{'shared_secret': VarIntPrefixedByteArray},
{'verify_token': VarIntPrefixedByteArray}]
state_login_serverbound = {
STATE_LOGIN_SERVERBOUND = {
0x00: LoginStartPacket,
0x01: EncryptionResponsePacket
}
@ -267,7 +267,7 @@ class SetCompressionPacketPlayState(Packet):
{'threshold': VarInt}]
state_playing_clientbound = {
STATE_PLAYING_CLIENTBOUND = {
0x00: KeepAlivePacket,
0x01: JoinGamePacket,
0x02: ChatMessagePacket,
@ -295,7 +295,7 @@ class PositionAndLookPacket(Packet):
{'pitch': Float},
{'on_ground': Boolean}]
state_playing_serverbound = {
STATE_PLAYING_SERVERBOUND = {
0x00: KeepAlivePacket,
0x01: ChatPacket,
0x06: PositionAndLookPacket

View File

@ -4,14 +4,13 @@ from minecraft.networking.encryption import minecraft_sha1_hash_digest
class Hashing(unittest.TestCase):
test_data = {'Notch': '4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48',
'jeb_': '-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1',
'simon': '88e16a1019277b15d58faf0541e11910eb756f6'}
'jeb_': '-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1',
'simon': '88e16a1019277b15d58faf0541e11910eb756f6'}
def test_hashing(self):
for input_value in self.test_data.iterkeys():
sha1_hash = hashlib.sha1()
sha1_hash.update(input_value)
self.assertEquals(minecraft_sha1_hash_digest(sha1_hash), self.test_data[input_value])
self.assertEquals(minecraft_sha1_hash_digest(sha1_hash),
self.test_data[input_value])