2019-01-22 01:30:42 +01:00
from . packet import Packet
from . EnumQuery import EnumQuery
from . EnumResponse import EnumResponse
from . CFrame import CFrame
from . DFrame import DFrame
from giants . masterserver import MasterServer
from . session import Session
import socket
import threading
import traceback
import uuid
from utils . logger import setup_logger
from giants . player import Player
logger = setup_logger ( __name__ )
class Netserver :
def __init__ ( self , server ) :
self . socket = None
self . statssocket = None
self . server = server
self . addrs = [ ]
self . guid = uuid . uuid4 ( ) . bytes
def run ( self ) :
self . socket = socket . socket ( socket . AF_INET , socket . SOCK_DGRAM )
self . socket . bind ( ( self . server . listen_ip , self . server . listen_port ) )
logger . debug ( " Listening to %s : %s " , self . server . listen_ip , self . server . listen_port )
gameserverthread = threading . Thread ( target = self . handle_packets )
gameserverthread . start ( )
#ms = MasterServer(self.server)
#statsserverthread = threading.Thread(target=ms.register_and_run)
#statsserverthread.start()
gameserverthread . join ( )
#statsserverthread.join()
def handle_packets ( self ) :
while True :
data , addr = self . socket . recvfrom ( 1024 )
logger . debug ( " %s : %s > %s " , addr [ 0 ] , addr [ 1 ] , data . hex ( ) )
self . handle_new_packet ( addr , data )
def handle_new_packet ( self , addr , bData ) :
pPacket = Packet ( bData )
first = pPacket . getByte ( )
if len ( bData ) > = 4 and first & DFrame . PACKET_COMMAND_DATA :
self . handle_new_dframe ( addr , pPacket )
elif len ( bData ) > = 12 and first & CFrame . PACKET_COMMAND_FRAME :
self . handle_new_cframe ( addr , pPacket )
elif first == EnumQuery . LEAD or first == EnumResponse . LEAD :
self . handle_new_enum ( addr , pPacket )
else :
logger . error ( " Unknown frame received " )
return
def handle_new_enum ( self , addr , data ) :
# EnumQuery or EnumResponse
second = data . getByte ( )
if second == EnumQuery . COMMAND :
# EnumQuery
try :
eq = EnumQuery ( data )
logger . debug ( " Got EnumQuery, sending EnumResponse " )
er = EnumResponse ( )
er . Payload = eq . Payload
er . ApplicationDescSize = 0x50
er . ApplicationDescFlags = 0x41
er . MaxPlayers = self . server . maxplayers
er . CurrentPlayers = len ( self . server . players )
er . SessionName = self . server . name
er . ApplicationInstanceGUID = self . guid
er . ApplicationGUID = eq . ApplicationGUID
er . ApplicationReservedData = b ' \xff ' # Map ID
er . ApplicationReservedData + = b ' \x00 \x04 \x00 '
er . ApplicationReservedData + = b ' \xd9 \x05 ' # game version
er . ApplicationReservedData + = b ' \x02 \x92 \x05 \x00 \x01 \x00 \x00 \x00 \x00 \x00 ' # Unknown
2019-01-22 10:35:15 +01:00
er . ApplicationReservedData + = b ' \x9c \x53 \xf4 \xde ' # Seems to be a checksum of current map
2019-01-22 01:30:42 +01:00
# \x00\x00\x00\x00: [None]
# \x00\x00\x00\x01: <custom map>
# \x9c\x53\xf4\xdd: Three Way Island - Canyons
# \x1e\xe9\x39\xe1: Still Winter
er . ApplicationReservedData + = self . server . currentmap . mapname . encode ( " ascii " )
er . ApplicationReservedData + = b ' \x00 ' * ( 32 - len ( self . server . currentmap . mapname ) )
self . send_packet ( addr , er . to_packet ( ) )
except Exception :
logger . error ( " Could not parse EnumQuery or forge EnumResponse: " )
traceback . print_exc ( )
return
elif second == EnumResponse . COMMAND :
# wait what? ignore that shit
return
else :
logger . error ( " Unknown DPLHP command: %s " , second )
def handle_new_cframe ( self , addr , data ) :
try :
cframe = CFrame ( data )
session = self . get_session ( addr )
if cframe . ExtOpCode == CFrame . FRAME_EXOPCODE_CONNECT :
# CONNECT CFRAME
if session and session . Full :
logger . error ( " Session %s : %s was already fully connected. Ignoring. " , session . ip , session . port )
return
elif session and not session . Full and session . SessID == cframe . SessID :
# send CONNECTED
logger . debug ( " Already partially established connection. Sending a CFRAME CONNECTED again. " )
session . send_cframe_connected ( cframe )
#session.setup_Connect_Retry_Timer()
return
if not self . server . accept_new_players :
# ignore new players
return
else :
logger . debug ( " New connection. Sending a CFRAME CONNECTED. " )
session = Session ( self . socket )
session . SessID = cframe . SessID
session . ip = addr [ 0 ]
session . port = addr [ 1 ]
self . addrs . append ( session )
session . send_cframe_connected ( cframe )
#session.setup_Connect_Retry_Timer()
elif cframe . ExtOpCode == CFrame . FRAME_EXOPCODE_CONNECTED :
# CONNECTED CFRAME
# check if already sent a CONNECT packet
logger . debug ( " %s : %s sent back a CONNECTED CFrame. " , session . ip , session . port )
if not session :
logger . error ( " %s sent a CONNECTED opcode without having sent a CONNECT before. GTFO. " , addr )
return
if not cframe . SessID == session . SessID or cframe . Command & CFrame . PACKET_COMMAND_POLL :
logger . error ( " Sent a CONNECTED packet with incorrect SessID or had COMMAND_POLL " )
return
session . Full = True # fully connected
session . cancel_Connect_Retry_Timer ( )
elif cframe . ExtOpCode == CFrame . FRAME_EXOPCODE_SACK :
if not session or not session . Full :
logger . error ( " Received a SACK packet for a non fully connected session " )
return
sack_sent = False
if not cframe . NSeq == session . next_expected_seq :
logger . error ( " Received CFRAME ( %s ) does not have the same NSeq ( %s ). Did we miss a packet? " , cframe . NSeq , session . next_expected_seq )
if not sack_sent :
session . send_cframe_sack ( )
sack_sent = True
2019-01-22 10:35:15 +01:00
if not session . next_send == cframe . NRecv :
2019-01-22 01:30:42 +01:00
logger . error ( " Received CFRAME ( %s ) does not have same NRcv ( %s ). One sent packet might have been lost. " , cframe . NRecv , session . CFRAME_NRcv )
if not sack_sent :
session . send_cframe_sack ( )
sack_sent = True
# WIP: The bNSeq, bNRcv, optional selective acknowledgment (SACK), and optional send mask fields are then processed by using the standard rules in sections 3.1.5.2.1 through 3.1.5.2.4
# TODO: A successfully validated SACK packet SHOULD count as a valid receive and thus restart the KeepAlive timer
if cframe . Command & CFrame . PACKET_COMMAND_POLL :
if not session :
logger . error ( " Received a POLL packet for a non fully connected session " )
return
logger . debug ( " Got a CFrame POLL. Replying with a SACK. " )
# must send back a ACK
session . send_cframe_sack ( )
except Exception :
logger . error ( " Should have been a CFRAME but could not parse it " )
traceback . print_exc ( )
return
def handle_new_dframe ( self , addr , data ) :
try :
dframe = DFrame ( data )
logger . debug ( " Received DFRAME " )
session = next ( ( x for x in self . addrs if x . ip == addr [ 0 ] and x . port == addr [ 1 ] ) , None )
if not session :
logger . debug ( " %s sent a DFRAME without having sent a CONNECT before. GTFO. " , addr )
logger . debug ( self . addrs )
return
if not dframe . Seq == session . next_expected_seq :
logger . error ( " %s unexpected SEQ. Got %s , expected %s " , addr , dframe . Seq , session . next_expected_seq )
#return
if dframe . Control & DFrame . PACKET_CONTROL_END_STREAM :
if not session :
logger . error ( " Received a END STREAM packet for a non fully connected session " )
return
# disconnect
resp = DFrame ( )
resp . Command = DFrame . PACKET_COMMAND_DATA | DFrame . PACKET_COMMAND_NEW_MSG | DFrame . PACKET_COMMAND_END_MSG | DFrame . PACKET_COMMAND_RELIABLE | DFrame . PACKET_COMMAND_SEQUENTIAL
resp . Control = DFrame . PACKET_CONTROL_END_STREAM
resp . Seq = dframe . NRcv
resp . NRcv = session . next_send
session . send ( resp )
session . send_cframe_sack ( )
# TODO: broadcast session has disconnected
self . addrs . remove ( session )
return
2019-01-22 10:35:15 +01:00
if dframe . Control & DFrame . PACKET_CONTROL_KEEPALIVE_OR_CORRELATE :
session . send_dframe_keepalive ( )
2019-01-22 01:30:42 +01:00
session . next_expected_seq + = 1
if dframe . Payload :
self . handle_game_packet ( session , dframe . Payload )
else :
pass
except Exception as e :
logger . error ( " Could not parse DFRAME: " )
traceback . print_exc ( )
return
def handle_game_packet ( self , session , payload ) :
logger . debug ( " Payload: %s " , payload )
if payload [ 0 ] == 0xc1 :
# connect frame?
_ = payload [ 0 : 4 ]
_ = payload [ 4 : 8 ]
_ = payload [ 8 : 12 ]
_ = payload [ 12 : 16 ]
_ = payload [ 16 : 20 ]
_ = payload [ 20 : 52 ]
instanceguid = payload [ 52 : 68 ]
gameguid = payload [ 68 : 84 ]
_ = payload [ 84 : 88 ]
_ = payload [ 88 : 92 ]
name = payload [ 92 : ]
name = name . decode ( " utf-16-le " ) [ 0 : - 1 ]
logger . debug ( " %s connected! " , name )
player = Player ( name , session )
self . server . new_player ( player )
session . send_dframe_keepalive ( )
r = DFrame ( )
r . Command = DFrame . PACKET_COMMAND_DATA | DFrame . PACKET_COMMAND_RELIABLE | DFrame . PACKET_COMMAND_SEQUENTIAL | DFrame . PACKET_COMMAND_POLL | DFrame . PACKET_COMMAND_NEW_MSG | DFrame . PACKET_COMMAND_END_MSG | DFrame . PACKET_COMMAND_USER_1
r . Control = 0x00
r . Seq = session . next_send
r . NRcv = session . next_expected_seq
payload = Packet ( )
payload . putLong ( 0xc2 ) # C2 = response to a C1 connection?
payload . putLong ( 0x00 ) # unknown
payload . putLong ( 0x00 ) # unknown
payload . putLong ( 0x50 ) # unknown - 80
payload . putLong ( 0x41 ) # unknown - 65
payload . putLong ( 0x14 ) # max players
payload . putLong ( 0x02 ) # current players
payload . write ( b ' \x0e \x01 \x00 \x00 ' ) # unknown - 3585 - or \x16\x01\x00\x00
payload . putLong ( len ( ( self . server . name + " \x00 " ) . encode ( " utf-16-le " ) ) ) # SERVERNAME LENGTH
for _ in range ( 4 ) :
payload . putLong ( 0 ) # unknown
payload . putLong ( 0xda ) # unknown - 218 - or 0xe2
payload . putLong ( 0x34 ) # unknown - 52
payload . write ( self . guid ) # instance guid
payload . write (
b " \x10 \x5e \x62 \xa7 \x96 \x1a \xd2 \x11 \x9a \xfc \x00 \x60 \x08 \x45 \xe5 \x71 " ) # app guid
payload . write ( b ' \x40 \xf5 \x62 \xe1 ' )
payload . putLong ( 0x21 ) # unknown - 33
payload . putLong ( 0x00 ) # unknown
payload . putLong ( 0x02 ) # unknown - 2
payload . putLong ( 0x00 )
payload . write ( b ' \x45 \xf5 \x52 \xe3 ' )
payload . putLong ( 0x00 )
payload . write ( b ' \x02 \x04 \x00 \x00 ' ) # unknown
payload . putLong ( 0x02 ) # unknown - 2
payload . putLong ( 0x00 ) # unknown
payload . putLong ( 0x07 ) # unknown - 7
for _ in range ( 6 ) :
payload . putLong ( 0 ) # unknown
payload . write ( b ' \x40 \xf5 \x62 \xe1 ' ) # unknown - 1089823457
payload . putLong ( 0x00 ) # unknown
payload . write ( b ' \x00 \x02 \x00 \x00 ' ) # unknown - ?
payload . putLong ( 0x21 ) # unknown - 33
payload . putLong ( 0x00 ) # unknown - 0
payload . putLong ( 0x07 ) # unknown - 7
payload . putLong ( 0xcc ) # unknown - 204
payload . putLong ( 0x0e ) # unknown - 6
for _ in range ( 4 ) :
payload . putLong ( 0 ) # unknown
payload . write ( ( name + " \x00 " ) . encode ( " utf-16-le " ) )
payload . write ( b ' \xff ' ) # map ID
payload . write ( b ' \x01 \x01 \x00 ' )
#payload.write(b'\x00\x04\x00')
payload . write ( b ' \xd9 \x05 ' ) # game version
2019-01-22 10:35:15 +01:00
payload . write ( b ' \x02 \x92 ' ) # unknown
payload . write ( b ' \x05 \x00 \x01 \x00 \x00 \x00 \x00 \x00 ' ) # Unknown
payload . write ( b ' \x9c \x53 \xf4 \xdf ' ) # Seems to be a checksum of current map OR linked to the number of chars in the map name
2019-01-22 01:30:42 +01:00
payload . write ( self . server . currentmap . mapname . encode ( " ascii " ) )
2019-01-22 10:35:15 +01:00
payload . write ( b ' \x00 ' * ( 32 - len ( self . server . currentmap . mapname ) ) )
#payload.putLong(0x00) # ??
2019-01-22 01:30:42 +01:00
payload . write ( ( self . server . name + " \x00 " ) . encode ( " utf-16-le " ) )
r . Payload = payload . getvalue ( )
session . send ( r )
2019-01-22 10:35:15 +01:00
elif payload [ 0 ] == 0xc3 :
player = self . get_player ( session )
self . server . broadcast_message ( " %s : you should never have joined... Sorry bro. " % player . name )
player . session . send_gamedata ( b ' \x3c \x56 \xab \x31 \x96 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' )
player . session . send_gamedata ( b ' \x01 \x02 \x57 \xab \xa1 \x96 \x56 \xab \x31 \x96 \x0e \x01 \x00 \x00 \x00 ' )
player . session . send_gamedata ( b ' \x3d \x00 \x5b \x53 \x65 \x72 \x76 \x65 \x72 \x5d ' ) # [SERVER]
player . session . send_gamedata ( b ' \x3d \x01 ' + player . name . encode ( " ascii " ) + b " \x00 " ) # playername
player . session . send_gamedata ( b ' \x0f \x56 \xab \x31 \x96 \x06 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ) # unknown
player . session . send_gamedata ( b ' \x10 \x02 \x56 \xab \x31 \x96 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ) # unknown
player . session . send_gamedata ( b ' \x29 \x28 \x00 \x00 \x80 \x32 \x00 \x00 \x80 \x05 \x00 \x00 \x80 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ) # unknown
player . session . send_gamedata ( b ' \x39 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ) # unknown
player . session . send_gamedata ( b ' \x39 \x01 \x02 \x01 \x02 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ) # unknown
player . session . send_gamedata ( b ' \x0f \x56 \xab \x31 \x96 \x07 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ) # unknown
player . session . send_gamedata ( b ' \x0a \x12 \x00 \x00 \x00 \x00 \x28 \x10 \x03 \x10 \x12 \x00 \x00 \x00 \xc1 \xff \x41 \xff \x00 ' , acknow = True ) # unknown
player . session . send_gamedata ( b ' \x0f \x56 \xab \x31 \x96 \x08 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' , acknow = True ) # unknown
2019-01-22 01:30:42 +01:00
def send_packet ( self , addr , packet ) :
self . socket . sendto ( packet . getvalue ( ) , addr )
logger . debug ( " %s : %s < %s " , addr [ 0 ] , addr [ 1 ] , packet . getvalue ( ) . hex ( ) )
def get_session ( self , iporaddr , port = None ) :
if not port :
return next ( ( x for x in self . addrs if x . ip == iporaddr [ 0 ] and x . port == iporaddr [ 1 ] ) , None )
else :
return next ( ( x for x in self . addrs if x . ip == iporaddr and x . port == port ) , None )
2019-01-22 10:35:15 +01:00
def get_player ( self , iporaddr , port = None ) :
if type ( iporaddr ) == Session :
return next ( ( x for x in self . server . players if x . session . ip == iporaddr . ip and x . session . port == iporaddr . port ) , None )
if not port :
return next ( ( x for x in self . server . players if x . session . ip == iporaddr [ 0 ] and x . session . port == iporaddr [ 1 ] ) , None )
else :
return next ( ( x for x in self . server . players if x . session . ip == iporaddr and x . session . port == port ) , None )