Added timeout send/receive

This commit is contained in:
Matteo ℱan 2020-09-24 22:44:15 +02:00
parent 990cd5e48f
commit 0aa63fa2cf
No known key found for this signature in database
GPG Key ID: 3C30A05BC133D9B6
3 changed files with 84 additions and 50 deletions

View File

@ -22,7 +22,7 @@ from pykms_RequestV5 import kmsRequestV5
from pykms_RequestV6 import kmsRequestV6 from pykms_RequestV6 import kmsRequestV6
from pykms_RpcBase import rpcBase from pykms_RpcBase import rpcBase
from pykms_DB2Dict import kmsDB2Dict from pykms_DB2Dict import kmsDB2Dict
from pykms_Misc import check_setup from pykms_Misc import check_setup, check_other
from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp
from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals
from pykms_Format import justify, byterize, enco, deco, pretty_printer from pykms_Format import justify, byterize, enco, deco, pretty_printer
@ -50,24 +50,28 @@ loggerclt = logging.getLogger('logclt')
# 'help' string - 'default' value - 'dest' string. # 'help' string - 'default' value - 'dest' string.
clt_options = { clt_options = {
'ip' : {'help' : 'The IP address or hostname of the KMS server.', 'def' : "0.0.0.0", 'des' : "ip"}, 'ip' : {'help' : 'The IP address or hostname of the KMS server.', 'def' : "0.0.0.0", 'des' : "ip"},
'port' : {'help' : 'The port the KMS service is listening on. The default is \"1688\".', 'def' : 1688, 'des' : "port"}, 'port' : {'help' : 'The port the KMS service is listening on. The default is \"1688\".', 'def' : 1688, 'des' : "port"},
'mode' : {'help' : 'Use this flag to manually specify a Microsoft product for testing the server. The default is \"Windows81\"', 'mode' : {'help' : 'Use this flag to manually specify a Microsoft product for testing the server. The default is \"Windows81\"',
'def' : "Windows8.1", 'des' : "mode", 'def' : "Windows8.1", 'des' : "mode",
'choi' : ["WindowsVista","Windows7","Windows8","Windows8.1","Windows10","Office2010","Office2013","Office2016","Office2019"]}, 'choi' : ["WindowsVista","Windows7","Windows8","Windows8.1","Windows10","Office2010","Office2013","Office2016","Office2019"]},
'cmid' : {'help' : 'Use this flag to manually specify a CMID to use. If no CMID is specified, a random CMID will be generated.', 'cmid' : {'help' : 'Use this flag to manually specify a CMID to use. If no CMID is specified, a random CMID will be generated.',
'def' : None, 'des' : "cmid"}, 'def' : None, 'des' : "cmid"},
'name' : {'help' : 'Use this flag to manually specify an ASCII machine name to use. If no machine name is specified a random one \ 'name' : {'help' : 'Use this flag to manually specify an ASCII machine name to use. If no machine name is specified a random one \
will be generated.', 'def' : None, 'des' : "machine"}, will be generated.', 'def' : None, 'des' : "machine"},
'time0' : {'help' : 'Set the maximum time to wait for a connection attempt to KMS server to succeed. Default is no timeout.',
'def' : None, 'des' : "timeoutidle"},
'time1' : {'help' : 'Set the maximum time to wait for sending / receiving a request / response. Default is no timeout.',
'def' : None, 'des' : "timeoutsndrcv"},
'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Deactivated by default.', 'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Deactivated by default.',
'def' : False, 'des' : "asyncmsg"}, 'def' : False, 'des' : "asyncmsg"},
'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", 'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel",
'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MININFO"]}, 'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MININFO"]},
'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logclient.log\". \ 'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logclient.log\". \
Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \ Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \
Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.', Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.',
'def' : os.path.join('.', 'pykms_logclient.log'), 'des' : "logfile"}, 'def' : os.path.join('.', 'pykms_logclient.log'), 'des' : "logfile"},
'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.', 'def' : 0, 'des': "logsize"}, 'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.', 'def' : 0, 'des': "logsize"},
} }
def client_options(): def client_options():
@ -82,6 +86,10 @@ def client_options():
help = clt_options['cmid']['help'], type = str) help = clt_options['cmid']['help'], type = str)
client_parser.add_argument("-n", "--name", dest = clt_options['name']['des'] , default = clt_options['name']['def'], client_parser.add_argument("-n", "--name", dest = clt_options['name']['des'] , default = clt_options['name']['def'],
help = clt_options['name']['help'], type = str) help = clt_options['name']['help'], type = str)
client_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = clt_options['time0']['des'], default = clt_options['time0']['def'],
help = clt_options['time0']['help'], type = str)
client_parser.add_argument("-t1", "--timeout-sndrcv", action = "store", dest = clt_options['time1']['des'], default = clt_options['time1']['def'],
help = clt_options['time1']['help'], type = str)
client_parser.add_argument("-y", "--async-msg", action = "store_true", dest = clt_options['asyncmsg']['des'], client_parser.add_argument("-y", "--async-msg", action = "store_true", dest = clt_options['asyncmsg']['des'],
default = clt_options['asyncmsg']['def'], help = clt_options['asyncmsg']['help']) default = clt_options['asyncmsg']['def'], help = clt_options['asyncmsg']['help'])
client_parser.add_argument("-V", "--loglevel", dest = clt_options['llevel']['des'], action = "store", client_parser.add_argument("-V", "--loglevel", dest = clt_options['llevel']['des'], action = "store",
@ -140,7 +148,11 @@ def client_check():
clt_config['call_id'] = 1 clt_config['call_id'] = 1
# Check other specific client options.
opts = [('timeoutidle', '-t0/--timeout-idle'),
('timeoutsndrcv', '-t1/--timeout-sndrcv')]
check_other(clt_config, opts, loggerclt, where = 'clt')
def client_update(): def client_update():
kmsdb = kmsDB2Dict() kmsdb = kmsDB2Dict()
@ -164,17 +176,24 @@ def client_update():
clt_config['KMSClientAppID'] = appitem['Id'] clt_config['KMSClientAppID'] = appitem['Id']
clt_config['KMSClientKMSCountedID'] = kmsitem['Id'] clt_config['KMSClientKMSCountedID'] = kmsitem['Id']
break break
def client_create(): def client_connect():
loggerclt.info("Connecting to %s on port %d..." % (clt_config['ip'], clt_config['port'])) loggerclt.info("Connecting to %s on port %d" % (clt_config['ip'], clt_config['port']))
try: try:
clt_sock = socket.create_connection((clt_config['ip'], clt_config['port'])) clt_sock = socket.create_connection((clt_config['ip'], clt_config['port']), timeout = clt_config['timeoutidle'])
loggerclt.info("Connection successful !") loggerclt.info("Connection successful !")
clt_sock.settimeout(clt_config['timeoutsndrcv'])
except socket.timeout:
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
put_text = "{reverse}{red}{bold}Client connection timed out. Exiting...{end}")
except (socket.gaierror, socket.error) as e: except (socket.gaierror, socket.error) as e:
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
put_text = "{reverse}{red}{bold}Connection failed '%s:%d': %s. Exiting...{end}" %(clt_config['ip'], put_text = "{reverse}{red}{bold}Connection failed '%s:%d': %s. Exiting...{end}" %(clt_config['ip'],
clt_config['port'], clt_config['port'],
str(e))) str(e)))
return clt_sock
def client_create(clt_sock):
binder = pykms_RpcBind.handler(None, clt_config) binder = pykms_RpcBind.handler(None, clt_config)
RPC_Bind = enco(str(binder.generateRequest()), 'latin-1') RPC_Bind = enco(str(binder.generateRequest()), 'latin-1')
@ -184,16 +203,16 @@ def client_create():
clt_sock.send(RPC_Bind) clt_sock.send(RPC_Bind)
except socket.error as e: except socket.error as e:
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
put_text = "{reverse}{red}{bold}While sending: %s{end}" %str(e)) put_text = "{reverse}{red}{bold}While sending: %s. Exiting...{end}" %str(e))
try: try:
bindResponse = clt_sock.recv(1024) bindResponse = clt_sock.recv(1024)
if bindResponse == '' or not bindResponse: if bindResponse == '' or not bindResponse:
pretty_printer(log_obj = loggerclt.warning, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.warning, to_exit = True, where = "clt",
put_text = "{reverse}{yellow}{bold}No data received.{end}") put_text = "{reverse}{yellow}{bold}No data received. Exiting...{end}")
pretty_printer(num_text = [-4, 7], where = "clt") pretty_printer(num_text = [-4, 7], where = "clt")
except socket.error as e: except socket.error as e:
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
put_text = "{reverse}{red}{bold}While receiving: %s{end}" %str(e)) put_text = "{reverse}{red}{bold}While receiving: %s. Exiting...{end}" %str(e))
packetType = MSRPCHeader(bindResponse)['type'] packetType = MSRPCHeader(bindResponse)['type']
if packetType == rpcBase.packetType['bindAck']: if packetType == rpcBase.packetType['bindAck']:
@ -209,13 +228,13 @@ def client_create():
clt_sock.send(RPC_Actv) clt_sock.send(RPC_Actv)
except socket.error as e: except socket.error as e:
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
put_text = "{reverse}{red}{bold}While sending: %s{end}" %str(e)) put_text = "{reverse}{red}{bold}While sending: %s. Exiting...{end}" %str(e))
try: try:
response = clt_sock.recv(1024) response = clt_sock.recv(1024)
pretty_printer(num_text = [-4, 20], where = "clt") pretty_printer(num_text = [-4, 20], where = "clt")
except socket.error as e: except socket.error as e:
pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt",
put_text = "{reverse}{red}{bold}While receiving: %s{end}" %str(e)) put_text = "{reverse}{red}{bold}While receiving: %s. Exiting...{end}" %str(e))
loggerclt.debug("Response: \n%s\n" % justify(deco(binascii.b2a_hex(response), 'latin-1'))) loggerclt.debug("Response: \n%s\n" % justify(deco(binascii.b2a_hex(response), 'latin-1')))
parsed = MSRPCRespHeader(response) parsed = MSRPCRespHeader(response)
@ -244,20 +263,28 @@ def client_create():
sys.exit(0) sys.exit(0)
else: else:
pretty_printer(log_obj = loggerclt.warning, to_exit = True, where = "clt", pretty_printer(log_obj = loggerclt.warning, to_exit = True, where = "clt",
put_text = "{reverse}{magenta}{bold}Something went wrong.{end}") put_text = "{reverse}{magenta}{bold}Something went wrong. Exiting...{end}")
def clt_main(with_gui = False): def clt_main(with_gui = False):
if not with_gui: try:
# Parse options. if not with_gui:
client_options() # Parse options.
client_options()
# Check options.
client_check() # Check options.
# Update Config. client_check()
client_update() # Update Config.
# Create and run client. client_update()
client_create() # Create and run client.
clt_sock = client_connect()
client_create(clt_sock)
except (KeyboardInterrupt, SystemExit):
try:
clt_sock.shutdown(socket.SHUT_RDWR)
clt_sock.close()
except:
pass
def createKmsRequestBase(): def createKmsRequestBase():
requestDict = kmsBase.kmsRequestStruct() requestDict = kmsBase.kmsRequestStruct()
requestDict['versionMinor'] = clt_config['KMSProtocolMinorVersion'] requestDict['versionMinor'] = clt_config['KMSProtocolMinorVersion']

View File

@ -552,6 +552,15 @@ def check_setup(config, options, logger, where):
pretty_printer(log_obj = logger.error, where = where, to_exit = True, pretty_printer(log_obj = logger.error, where = where, to_exit = True,
put_text = "{reverse}{red}{bold}Port number '%s' is invalid. Enter between 1 - 65535. Exiting...{end}" %config['port']) put_text = "{reverse}{red}{bold}Port number '%s' is invalid. Enter between 1 - 65535. Exiting...{end}" %config['port'])
def check_other(config, options, logger, where):
for dest, stropt in options:
try:
config[dest] = int(config[dest])
except:
if config[dest] is not None:
pretty_printer(log_obj = logger.error, where = where, to_exit = True,
put_text = "{reverse}{red}{bold}argument `%s`: invalid with: '%s'. Exiting...{end}" %(stropt, config[dest]))
#------------------------------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------------------------------
# http://joshpoley.blogspot.com/2011/09/hresults-user-0x004.html (slerror.h) # http://joshpoley.blogspot.com/2011/09/hresults-user-0x004.html (slerror.h)

View File

@ -18,7 +18,7 @@ from time import monotonic as time
import pykms_RpcBind, pykms_RpcRequest import pykms_RpcBind, pykms_RpcRequest
from pykms_RpcBase import rpcBase from pykms_RpcBase import rpcBase
from pykms_Dcerpc import MSRPCHeader from pykms_Dcerpc import MSRPCHeader
from pykms_Misc import check_setup, check_lcid, check_dir from pykms_Misc import check_setup, check_lcid, check_dir, check_other
from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp
from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals, kms_parser_check_connect from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals, kms_parser_check_connect
from pykms_Format import enco, deco, pretty_printer, justify from pykms_Format import enco, deco, pretty_printer, justify
@ -202,6 +202,8 @@ The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID
'def' : "364F463A8863D35F", 'des' : "hwid"}, 'def' : "364F463A8863D35F", 'des' : "hwid"},
'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.', 'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.',
'def' : None, 'des' : "timeoutidle"}, 'def' : None, 'des' : "timeoutidle"},
'time1' : {'help' : 'Set the maximum time to wait for sending / receiving a request / response. Default is no timeout.',
'def' : None, 'des' : "timeoutsndrcv"},
'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Deactivated by default.', 'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Deactivated by default.',
'def' : False, 'des' : "asyncmsg"}, 'def' : False, 'des' : "asyncmsg"},
'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", 'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel",
@ -238,6 +240,8 @@ def server_options():
help = srv_options['hwid']['help'], type = str) help = srv_options['hwid']['help'], type = str)
server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'], server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'],
help = srv_options['time0']['help'], type = str) help = srv_options['time0']['help'], type = str)
server_parser.add_argument("-t1", "--timeout-sndrcv", action = "store", dest = srv_options['time1']['des'], default = srv_options['time1']['def'],
help = srv_options['time1']['help'], type = str)
server_parser.add_argument("-y", "--async-msg", action = "store_true", dest = srv_options['asyncmsg']['des'], server_parser.add_argument("-y", "--async-msg", action = "store_true", dest = srv_options['asyncmsg']['des'],
default = srv_options['asyncmsg']['def'], help = srv_options['asyncmsg']['help']) default = srv_options['asyncmsg']['def'], help = srv_options['asyncmsg']['help'])
server_parser.add_argument("-V", "--loglevel", action = "store", dest = srv_options['llevel']['des'], choices = srv_options['llevel']['choi'], server_parser.add_argument("-V", "--loglevel", action = "store", dest = srv_options['llevel']['des'], choices = srv_options['llevel']['choi'],
@ -478,20 +482,13 @@ def server_check():
srv_config['sqlite'] = False srv_config['sqlite'] = False
# Check other specific server options. # Check other specific server options.
list_dest = ['clientcount', 'timeoutidle'] opts = [('clientcount', '-c/--client-count'),
list_opt = ['-c/--client-count', '-t0/--timeout-idle'] ('timeoutidle', '-t0/--timeout-idle'),
('timeoutsndrcv', '-t1/--timeout-sndrcv')]
if serverthread.with_gui: if serverthread.with_gui:
list_dest += ['activation', 'renewal'] opts += [('activation', '-a/--activation-interval'),
list_opt += ['-a/--activation-interval', '-r/--renewal-interval'] ('renewal', '-r/--renewal-interval')]
check_other(srv_config, opts, loggersrv, where = 'srv')
for dest, opt in zip(list_dest, list_opt):
try:
srv_config[dest] = int(srv_config[dest])
except:
if srv_config[dest] is not None:
pretty_printer(log_obj = loggersrv.error, to_exit = True,
put_text = "{reverse}{red}{bold}argument `%s`: invalid with: '%s'. Exiting...{end}" %(opt, srv_config[dest]))
# Check further addresses / ports. # Check further addresses / ports.
if 'listen' in srv_config: if 'listen' in srv_config:
@ -602,6 +599,7 @@ class kmsServerHandler(socketserver.BaseRequestHandler):
srv_config['raddr'] = self.client_address srv_config['raddr'] = self.client_address
def handle(self): def handle(self):
self.request.settimeout(srv_config['timeoutsndrcv'])
while True: while True:
# self.request is the TCP socket connected to the client # self.request is the TCP socket connected to the client
try: try: