Skip to content

Commit 76daa86

Browse files
committed
bugfix: don't crash when a client sends bad data
1 parent 1a65666 commit 76daa86

File tree

2 files changed

+97
-95
lines changed

2 files changed

+97
-95
lines changed

src/networking/connection.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ def initialize_socket(self, sock):
5252

5353
def destroy_socket(self):
5454
try:
55-
self.socket.close()
55+
if self.socket:
56+
self.socket.close()
5657
self.socket = None
5758
self.stream = None
5859
print("Socket shutdown and closed.", flush=True)
@@ -123,6 +124,9 @@ def run_handler(self):
123124
self.packet_handler = self.packet_handler.next_handler()
124125

125126
self.packet_handler.handle()
127+
else:
128+
# Clean up if we can't even setup the handler
129+
self.on_disconnect()
126130

127131
def run(self):
128132
self.run_handler()

src/networking/packet_handler/clientbound/login_handler.py

Lines changed: 92 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from src.networking.packets.clientbound import HeldItemChange as HeldItemChangeClientbound
88
from src.networking.packets.clientbound import PlayerAbilities as PlayerAbilitiesClientbound
99

10+
from src.networking.packets.exceptions import InvalidPacketID
11+
1012
from cryptography.hazmat.backends import default_backend
1113
from cryptography.hazmat.primitives.asymmetric import rsa
1214
from cryptography.hazmat.primitives import serialization
@@ -60,118 +62,114 @@ def handle_position(self, packet):
6062
self.mc_connection.game_state.release()
6163

6264
def join_world(self):
63-
self.mc_connection.game_state.acquire()
65+
# If there's an exception releasing a lock actually happens this way
66+
with self.mc_connection.game_state.state_lock:
67+
# Send the player all the packets that lets them join the world
68+
for id_ in self.mc_connection.game_state.join_ids:
69+
if id_ in self.mc_connection.game_state.packet_log:
70+
packet = self.mc_connection.game_state.packet_log[id_]
71+
self.connection.send_packet_buffer_raw(packet.compressed_buffer)
72+
73+
# Send their player abilities
74+
self.connection.send_packet_raw(self.mc_connection.game_state.abilities)
75+
76+
# Send them their last position/look if it exists
77+
if PlayerPositionAndLookClientbound.id in self.mc_connection.game_state.packet_log:
78+
if self.mc_connection and self.mc_connection.game_state.last_pos_packet:
79+
last_packet = self.mc_connection.game_state.last_pos_packet
80+
81+
pos_packet = PlayerPositionAndLookClientbound( \
82+
X=last_packet.X, Y=last_packet.Y, Z=last_packet.Z, \
83+
Yaw=self.mc_connection.game_state.last_yaw, Pitch=self.mc_connection.game_state.last_pitch, Flags=0, \
84+
TeleportID=self.mc_connection.game_state.teleport_id)
85+
self.mc_connection.game_state.teleport_id += 1
86+
self.connection.send_packet_raw(pos_packet)
87+
else:
88+
self.connection.send_packet_buffer_raw(
89+
self.mc_connection.game_state.packet_log[PlayerPositionAndLookClientbound.id] \
90+
.compressed_buffer) # Send the last packet that we got
6491

65-
# Send the player all the packets that lets them join the world
66-
for id_ in self.mc_connection.game_state.join_ids:
67-
if id_ in self.mc_connection.game_state.packet_log:
68-
packet = self.mc_connection.game_state.packet_log[id_]
69-
self.connection.send_packet_buffer_raw(packet.compressed_buffer)
70-
71-
# Send their player abilities
72-
self.connection.send_packet_raw(self.mc_connection.game_state.abilities)
73-
74-
# Send them their last position/look if it exists
75-
if PlayerPositionAndLookClientbound.id in self.mc_connection.game_state.packet_log:
76-
if self.mc_connection and self.mc_connection.game_state.last_pos_packet:
77-
last_packet = self.mc_connection.game_state.last_pos_packet
78-
79-
pos_packet = PlayerPositionAndLookClientbound( \
80-
X=last_packet.X, Y=last_packet.Y, Z=last_packet.Z, \
81-
Yaw=self.mc_connection.game_state.last_yaw, Pitch=self.mc_connection.game_state.last_pitch, Flags=0, \
82-
TeleportID=self.mc_connection.game_state.teleport_id)
83-
self.mc_connection.game_state.teleport_id += 1
84-
self.connection.send_packet_raw(pos_packet)
85-
else:
86-
self.connection.send_packet_buffer_raw(
87-
self.mc_connection.game_state.packet_log[PlayerPositionAndLookClientbound.id] \
88-
.compressed_buffer) # Send the last packet that we got
89-
90-
if TimeUpdate.id in self.mc_connection.game_state.packet_log:
91-
self.connection.send_packet_buffer_raw(self.mc_connection.game_state.packet_log\
92-
[TimeUpdate.id].compressed_buffer)
93-
94-
# Send the player list items (to see other players)
95-
self.connection.send_single_packet_dict(self.mc_connection.game_state.player_list)
96-
97-
# Send all loaded chunks
98-
print("Sending chunks", flush=True)
99-
self.connection.send_single_packet_dict(self.mc_connection.game_state.chunks)
100-
print("Done sending chunks", flush=True)
101-
102-
# Send the player all the currently loaded entities
103-
self.connection.send_single_packet_dict(self.mc_connection.game_state.entities)
104-
105-
# Player sends ClientStatus, this is important for respawning if died
106-
self.mc_connection.send_packet_raw(ClientStatus(ActionID=0))
107-
108-
# Send their last held item
109-
self.connection.send_packet_raw(HeldItemChangeClientbound(Slot=self.mc_connection.game_state.held_item_slot))
110-
111-
# Send their current game state
112-
self.connection.send_packet_raw(GameState(Reason=self.mc_connection.game_state.gs_reason,\
113-
Value=self.mc_connection.game_state.gs_value))
114-
# Send their inventory
115-
self.connection.send_single_packet_dict(self.mc_connection.game_state.main_inventory)
92+
if TimeUpdate.id in self.mc_connection.game_state.packet_log:
93+
self.connection.send_packet_buffer_raw(self.mc_connection.game_state.packet_log\
94+
[TimeUpdate.id].compressed_buffer)
11695

117-
self.mc_connection.game_state.release()
96+
# Send the player list items (to see other players)
97+
self.connection.send_single_packet_dict(self.mc_connection.game_state.player_list)
98+
99+
# Send all loaded chunks
100+
print("Sending chunks", flush=True)
101+
self.connection.send_single_packet_dict(self.mc_connection.game_state.chunks)
102+
print("Done sending chunks", flush=True)
103+
104+
# Send the player all the currently loaded entities
105+
self.connection.send_single_packet_dict(self.mc_connection.game_state.entities)
106+
107+
# Player sends ClientStatus, this is important for respawning if died
108+
self.mc_connection.send_packet_raw(ClientStatus(ActionID=0))
109+
110+
# Send their last held item
111+
self.connection.send_packet_raw(HeldItemChangeClientbound(Slot=self.mc_connection.game_state.held_item_slot))
112+
113+
# Send their current game state
114+
self.connection.send_packet_raw(GameState(Reason=self.mc_connection.game_state.gs_reason,\
115+
Value=self.mc_connection.game_state.gs_value))
116+
# Send their inventory
117+
self.connection.send_single_packet_dict(self.mc_connection.game_state.main_inventory)
118118

119119
def setup(self):
120-
print("Reading handshake", flush=True)
120+
try:
121+
print("Reading handshake", flush=True)
121122

122-
pkt = self.read_packet_from_stream()
123-
if pkt is None:
124-
return False
125-
Handshake().read(pkt.packet_buffer)
123+
Handshake().read(self.read_packet_from_stream().packet_buffer)
126124

127-
print("Reading login start", flush=True)
128-
pkt = self.read_packet_from_stream()
129-
if pkt is None:
130-
return False
131-
LoginStart().read(pkt.packet_buffer)
125+
print("Reading login start", flush=True)
126+
LoginStart().read(self.read_packet_from_stream().packet_buffer)
132127

133-
# Generate a dummy (pubkey, privkey) pair
134-
privkey = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
135-
pubkey = privkey.public_key().public_bytes(encoding=serialization.Encoding.DER,
136-
format=serialization.PublicFormat.SubjectPublicKeyInfo)
128+
# Generate a dummy (pubkey, privkey) pair
129+
privkey = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
130+
pubkey = privkey.public_key().public_bytes(encoding=serialization.Encoding.DER,
131+
format=serialization.PublicFormat.SubjectPublicKeyInfo)
137132

138-
print("Trying to send encryption request", flush=True)
139-
self.connection.send_packet_raw(
140-
EncryptionRequest(ServerID='', PublicKey=pubkey, VerifyToken=self.mc_connection.VerifyToken))
133+
print("Trying to send encryption request", flush=True)
134+
self.connection.send_packet_raw(
135+
EncryptionRequest(ServerID='', PublicKey=pubkey, VerifyToken=self.mc_connection.VerifyToken))
141136

142-
print("Encryption request sent", flush=True)
137+
print("Encryption request sent", flush=True)
143138

144-
# The encryption response will be encrypted with the server's public key
145-
# Luckily, when this goes wrong read_packet returns None
146-
_ = self.read_packet_from_stream()
139+
# The encryption response will be encrypted with the server's public key
140+
# Luckily, when this goes wrong read_packet returns None
141+
_ = self.read_packet_from_stream()
147142

148-
if _ is None:
149-
print("Invalid encryption response!", flush=True)
150-
self.connection.on_disconnect()
151-
return False
143+
if _ is None:
144+
print("Invalid encryption response!", flush=True)
145+
self.connection.on_disconnect()
146+
return False
152147

153-
encryption_response = EncryptionResponse().read(_.packet_buffer)
148+
encryption_response = EncryptionResponse().read(_.packet_buffer)
154149

155-
# Decrypt and verify the verify token
156-
verify_token = privkey.decrypt(encryption_response.VerifyToken, PKCS1v15())
157-
assert (verify_token == self.mc_connection.VerifyToken)
150+
# Decrypt and verify the verify token
151+
verify_token = privkey.decrypt(encryption_response.VerifyToken, PKCS1v15())
152+
assert (verify_token == self.mc_connection.VerifyToken)
158153

159-
# Decrypt the shared secret
160-
shared_secret = privkey.decrypt(encryption_response.SharedSecret, PKCS1v15())
154+
# Decrypt the shared secret
155+
shared_secret = privkey.decrypt(encryption_response.SharedSecret, PKCS1v15())
161156

162-
# Enable encryption using the shared secret
163-
self.connection.enable_encryption(shared_secret)
157+
# Enable encryption using the shared secret
158+
self.connection.enable_encryption(shared_secret)
164159

165-
# Enable compression and assign the threshold to the connection
166-
if self.mc_connection.compression_threshold >= 0:
167-
self.connection.send_packet_raw(SetCompression(Threshold=self.mc_connection.compression_threshold))
168-
self.connection.compression_threshold = self.mc_connection.compression_threshold
160+
# Enable compression and assign the threshold to the connection
161+
if self.mc_connection.compression_threshold >= 0:
162+
self.connection.send_packet_raw(SetCompression(Threshold=self.mc_connection.compression_threshold))
163+
self.connection.compression_threshold = self.mc_connection.compression_threshold
169164

170-
self.connection.send_packet_raw(self.mc_connection.login_success)
165+
self.connection.send_packet_raw(self.mc_connection.login_success)
171166

172-
print("Joining world", flush=True)
173-
self.join_world()
174-
print("Finished joining world", flush=True)
167+
print("Joining world", flush=True)
168+
self.join_world()
169+
print("Finished joining world", flush=True)
170+
except (ValueError, EOFError, InvalidPacketID, ConnectionRefusedError, ConnectionAbortedError, \
171+
ConnectionResetError):
172+
return False
175173

176174
# Let the real connection know about our client
177175
# Now the client can start receiving forwarded data

0 commit comments

Comments
 (0)