Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions rcon_mc/lib/msocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ def __init__(self, host, port, *timeout):
self.host = host
self.port = port
self.connection = False

if timeout:
self.timeout = timeout[0]
else:
self.timeout = 1

self.error_stack = []

assert type(self.host) is StringType, "{m}{h}".format(
m="hostname is not a string:", h=self.host)
assert type(self.port) is IntType, "{m}{p}".format(
Expand All @@ -39,6 +42,7 @@ def __init__(self, host, port, *timeout):
def _manage_socket_error(self, ret_val):
if self.connection:
self.connection.close()

self.connection = None
self.error_stack.append(ret_val)
raise MSocketError(str(self.error_stack))
Expand All @@ -47,26 +51,31 @@ def connect(self):
"""Resolve remote host and connect however possible (IPV6 compat)"""
if self.connection:
return self.connection

for con in socket.getaddrinfo(self.host,
self.port,
socket.AF_UNSPEC,
socket.SOCK_STREAM):
addr_fam, sock_type, proto, canonical_name, server_addr = con

try:
self.connection = socket.socket(addr_fam, sock_type, proto)
except socket.error as ret_val:
self._manage_socket_error(ret_val)
continue

try:
self.connection.settimeout(self.timeout)
except(socket.error) as ret_val:
self._manage_socket_error(ret_val)
continue

try:
self.connection.connect(server_addr)
except(socket.error) as ret_val:
self._manage_socket_error(ret_val)
continue

if not self.connection:
continue
break
Expand Down Expand Up @@ -105,29 +114,41 @@ def receive(self, *buflen):
"""
not_ready = 0
packet = ""

if not buflen:
buflen = 1024 # rather arbitrary read amount

packet_size = 0

while True:

if self.connection is not None or False:
rdy = select.select([self.connection.fileno()], [], [], .3)[0]

if rdy:

try:
cpacket = self.connection.recv(buflen)

if not cpacket:
break

except(socket.error) as ret_val:
self._manage_socket_error(ret_val)
return False

cpacket_size = len(cpacket)
packet_size = packet_size + cpacket_size

if packet_size > 0:
packet = packet + cpacket
else:
packet = cpacket

continue
else:
not_ready = not_ready + 1

if not_ready > 2:
break
break
Expand All @@ -142,14 +163,17 @@ def manage(self, packet):
except(error):
print error
return False

try:
self.send(packet)
except(error):
print error
return False

try:
response = self.receive()
except(error):
print error
return False

return response
32 changes: 28 additions & 4 deletions rcon_mc/rcon.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ def __init__(self, host, port, password):
self.error_stack = []
self.id = 0
self.authenticated = False

assert type(self.host) is StringType, "{m}{h}".format(
m="hostname is not a string:",
h=self.host)
assert type(self.port) is IntType, "{m}{p}".format(
m="port is not a number:",
p=self.port)
assert type(self.password) is StringType, "password is not a string"

try:
self.connection = msocket.msocket(self.host, self.port)
except(msocket.error) as ret_val:
Expand All @@ -70,20 +72,23 @@ def _connect(self):
except(msocket.error) as ret_val:
if con is False:
self._manage_rcon_error(ret_val)
return false
return False
return True

def _pack_data(self, type_name, msg):
"""private method for crafting RCON requests"""
if not msg:
msg = ""

msg_len = len(msg)
size = msg_len + MIN_PACKET_SIZE

if msg_len > MAX_BODY_SIZE:
self._manage_rcon_error("{m}\n{s}".format(
m="Request message body too large. MAX=",
s=str(MAX_BODY_SIZE)))
return False

try:
request = "{s}{i}{t}{m}{n1}{n2}".format(
s=struct.pack('<i', size),
Expand All @@ -95,52 +100,63 @@ def _pack_data(self, type_name, msg):
m="Unable to pack data into TCP request: ",
s=str(self.error_stack)))
return False

return request

def _unpack_data(self, response):
"""private method to unpack the data and return the ascii message"""
response_size = len(response)
msg_size = response_size - MIN_PACKET_ACTUAL_SIZE

if response_size == 0:
self._manage_rcon_error("Zero length response from server")
return False

unpack_fmt = "{i}{m}{n}".format(i="<iii", m=str(msg_size), n="sxx")

try:
payload = struct.unpack(unpack_fmt, response)
except(struct.error) as ret_val:
self._manage_rcon_error(ret_val)
return False

size = payload[0]
payload_id = payload[1]
type_name = payload[2]
msg = payload[3]

return (payload_id, type_name, msg)

def _send(self, type_name, msg):
"""private send method for handling the dirty work in
sending a request"""

if not self.connection:
try:
self._connect()
except(error) as ret_val:
return False
return False

try:
request = self._pack_data(type_name, msg)
except(error) as ret_val:
return False

try:
response = self.connection.manage(request)
except(msocket.error) as ret_val:
self._manage_rcon_error(ret_val)
return false
return False

if not response:
self._manage_rcon_error("Empty response from server")
return false
return False

try:
response = self._unpack_data(response)
except(error) as ret_val:
return false
return False
return response

def _authenticate(self):
Expand All @@ -149,22 +165,27 @@ def _authenticate(self):
response = self._send(SERVERDATA_AUTH, self.password)
except(error) as ret_val:
return False

if response[0] == -1:
self._manage_rcon_error("Authentication failure")
return False

self.authenticated = True

return True

def send(self, msg):
"""API user function Sends a command to the RCON server,
does not necessarily disconnect. Returns the response.
"""
self.id = self.id + 1

if not self.authenticated:
try:
self._authenticate()
except Exception:
return False

try:
response = self._send(SERVERDATA_EXECCOMMAND, msg)
except Exception:
Expand All @@ -174,14 +195,17 @@ def send(self, msg):

if response[0] == -1:
self.authenticated = False

try:
self._authenticate()
except Exception:
return False

try:
response = self._send(SERVERDATA_EXECCOMMAND, msg)
except Exception:
return False

return response_msg

def disconnect(self):
Expand Down