Пример #1
0
class Server(parent_class):
	def __init__(self,load_mode='demo'):
		super(Server, self).__init__()
		self.threads = []
		self.open_port()

		#
		# user keys
		#
		mcfg = json.load(open(conf.conf_dir + conf.conf_main))
		self.my_name = mcfg['username']
		try:
			self.private,self.public = encrypt.import_keys(conf.key_dir+mcfg['private_key'],conf.key_dir+mcfg['public_key'])
		except Exception as e:
			self.log('cannot find keys: (%s, %s) %s'%(conf.key_dir+mcfg['private_key'],conf.key_dir+mcfg['public_key'],e))
			sys.exit()


		#
		# runtime tables:
		# current active connections
		#
		self.__client_socket  = {} 
		self.__socket_clinet  = {}
		
		#
		# databes of existing users and their keys
		#
		self.blockchain = Blockchain()

		self.network_state = 0
		if load_mode == 'full-load':
			self.history = History()

	def __del__(self):
		try:
			self.socket.close()
		except:
			pass


	def generate_body_id(self):
		def random_item(az):
			i = int(random.random()*(len(az)-1))
			return az[i]
		CHARS =  ''
		for i in ( range(65,91) + range(48,58) + range(97,123) ):
			CHARS += chr(i)
		st = ''
		for i in range(10):
			st += random_item(CHARS)
		return st

	#
	# create listening socket
	#
	def open_port(self):
		port = conf.port
		host = '' # here we can make some restrictions on connections # but we will not
		self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.socket.bind((host, port))
		self.socket.listen(5)
		self.socket.settimeout(30*3600)


	def check_threads(self):
		if len(self.threads) <= 0:
			return
		i = 0
		while i < len(self.threads):
			if not self.threads[i].is_alive():
				self.threads.pop(i).join()
			else:
				i += 1

	#######################################
	##    FIRST LEVEL OF DMS PROTOCOL    ##
	#######################################
	#
	# low-level send msg
	#
	def __send(self,sock,msg,pub_key):
		max_size = 500
		if type(msg) == str:
			msg = msg.decode()
		if len(msg) <= max_size:
			msg = [encrypt.encrypt_data(pub_key,msg)]
		else:
			n = int(len(msg) / max_size) + 1
			_msg = []
			for i in range(n+1):
				st = ''
				for k in range(max_size):
					if len(msg) > 0:
						st += msg.pop(0)
					else:
						break
				if len(st)>0:
					_msg.append(encrypt.encrypt_data(pub_key,st))
			msg = msg
		query = ''
		for i in range(len(msg)):
			query += asc.b2a_base64(str(len(msg[i])).encode())[:-1] 
			if i == len(msg):
				query += b','
			else:
				query += b'.'
			query += msg[i]
		self.debug('send: %s'%query)
		sock.send(query)

	#
	# low-level recv msg 
	#
	def __recv(self,sock,priv_key):
		query = b''
		q = b''
		boo = True
		while boo:
			while q not in [b'.',b',']:
				q = sock.recv(1)
				query += q
			boo = q == b','
			msg = sock.recv(int(asc.a2b_base64(encrypt.decrypt_data(priv_key,query[:-1])).decode()))
		self.debug('recv: %s'%msg)
		return msg

	#######################################
	##   SECOND LEVEL OF DMS PROTOCOL    ##
	#######################################

	#
	# takes dict (2lv), covert to str , crypt
	# and send it to peer
	#
	def send_2lv(self,transport_message):
		peer = transport_message['to']
		if self.blockchain.peer_status(peer) != None: 
			pub = self.blockchain.get_key(peer)
			self.__send(json.dumps(transport_message).encode())
			return True
		else:
			return False

	#
	# recv msg from peer & decrypt it 
	# return transport-message (dict)
	#
	def recv_2lv(self,conn):
		try:
			transport_message = json.loads(self.__recv(conn,self.private))
			return transport_message
		except Exception as e:
			self.log('ERROR: recv_2lv: %s'%e)


	######################################
	##   THIRD LEVEL OF DMS PROTOCOL    ##
	######################################

	
	#
	# takes pub-key and dict , convert to str , encrypt using pub-key , decode to str and return str
	#
	def __3lv_crypt(self,pub,dd):
		res = asc.b2a_base64(encrypt.encrypt_data(pub,json.dumps(dd)))
		if type(res) == bytes:
			res = res.decode()
		return res
	
	#
	# takes str in base64 , encode , decrypt , convert to dict
	#
	def __3lv_norm(self,st):
		if type(st) == str:
			st = st.encode()
		res = encrypt.decrypt_data(self.private,asc.a2b_base64(st))
		if type(res) == bytes:
			res = res.decode()
		return json.loads(res)

	#
	# return True if msg send
	# or False in case of error
	#
	def __send_3lv_(self,peer,body_obj):
		if self.blockchain.exists(peer):
			pub = self.blockchain.get_key(peer)
			body_obj['sign'] = encrypt.data_to_signature(self.private,encrypt.md5(json.dumps(body_obj)))
			self.__3lv_crypt(pub,body_obj)
			obj = {
				'from':self.my_name,
				'to':peer,
				'flow-controll':(self.network_state << 2),
				'data':[body_obj]
			}
			return self.send_2lv(obj)
		else:
			return False

	#
	# registrate new user
	#
	def registrate(self):
		username = self.my_name
		pass # FIXME

	# 
	# gets 2lv package and decide to reg or auth
	# return True if user authorized or registrated
	# or False in case of error
	# 
	def auth_ot_reg(self,data):
		return True # if he is not Vasya then we will not understand our reply
		
	#
	# send message to user
	#
	def send_user_message(self,username,message):
		bobj = {
			'part':conf.USER_PART_ID,
			'id':1,
			'data':{
				'from':self.my_name,
				'message':message
			}
		}
		self.__send_3lv_(username,bobj)

	def db_update_request(self,username):
		bobj = {
			'part':conf.BLCKCHN_PART_ID,
			'id':1, # ask for number of blocks and last hash
			'data':{
				'from':self.my_name
			}
		}
		self.__send_3lv_(username,bobj)

	#
	# handler for part of messages
	#
	def __3lv_error_part_handler(self,hightest_msg,body_id):
		self.log('ERROR: [%s] [errnum: %s] %s'%(hightest_msg['data']['from'],hightest_msg['data']['body_id'],hightest_msg['data']['error_num'],hightest_msg['data']['error_msg']))
	
	#
	# handler for part of messages
	#
	def __3lv_user_part_handler(self,hightest_msg,body_id):
		if hightest_msg['id'] == 1: # incoming message
			peer = hightest_msg['data']['from']
			msg  = hightest_msg['data']['message']
			self.extra('[%s][%s] %s -- NEW MSG -- %s'%(time.ctime(),body_id,peer,msg))
			reply = {
				'part':conf.USER_PART_ID,
				'id':1,
				'data':{
					'from':self.my_name,
					'body-id':body_id
				}
			}
			self.__send_3lv_(peer,reply)
		elif hightest_msg['id'] == 2: # delivered
			peer = hightest_msg['data']['from']
			bid  = hightest_msg['data']['body-id']
			self.extra('[%s][%s] %s -- MSG DELIVERED [%s] '%(time.ctime(),body_id,peer,bid))
		elif hightest_msg['id'] == 3: # delivered
			peer = hightest_msg['data']['from']
			bid  = hightest_msg['data']['body-id']
			self.extra('[%s][%s] %s -- MSG READ [%s] '%(time.ctime(),body_id,peer,bid))
	
	#
	# handler for part of messages
	#
	def __3lv_blckchn_part_handler(self,hightest_msg,body_id):
		reply = None
		peer = None
		if hightest_msg['id'] == 1: # ask for number of blocks and last hash
			peer = hightest_msg['data']['from']
			self.extra('[%s][%s] %s -- BLCKCHN 1 -- %s'%(time.ctime(),body_id,peer,msg))
			reply = {
				'part':conf.BLCKCHN_PART_ID,
				'id':2,
				'data':{
					'from':self.my_name,
					'count':self.blockchain.count(),
					'hash':self.blockchain.last_hash(),
				}
			}
		elif hightest_msg['id'] == 2: # we get number of blocks (count) and last hash (hash)
			peer = hightest_msg['data']['from']
			self.extra('[%s][%s] %s -- BLCKCHN 2 -- %s'%(time.ctime(),body_id,peer,msg))
			count 		= hightest_msg['data']['count'] 
			last_hash 	= hightest_msg['data']['last_hash']
			if count > self.blockchain.count():
				reply = { # ask for blocks
					'part':conf.BLCKCHN_PART_ID,
					'id':3 ,
					'data':{
						'from':self.my_name,
						'max':self.blockchain.count(),
						'min':count,
					}
				}
		elif hightest_msg['id'] == 3: # we get request for blocks from (min) till (max)
			peer = hightest_msg['data']['from']
			self.extra('[%s][%s] %s -- BLCKCHN 3 -- %s'%(time.ctime(),body_id,peer,msg))
			_min = hightest_msg['data']['min'] 
			_max = hightest_msg['data']['max']
			blocks = []
			for i in range(_min,_max):
				blocks.append(self.blockchain.blocks[i]._export())
			reply = { # ask for blocks
				'part':conf.BLCKCHN_PART_ID,
				'id':4,
				'data':{
					'from':self.my_name,
					'blocks':blocks
				}
			}
		elif hightest_msg['id'] == 4: # we get list of blocks
			peer = hightest_msg['data']['from']
			self.extra('[%s][%s] %s -- BLCKCHN 4 -- %s'%(time.ctime(),body_id,peer,msg))
			blocks = hightest_msg['data']['blocks'] 
			try:
				for i in blocks:
					blck = Block()
					blck._import(i)
					self.blockchain.add_block(blck)
			except Exception as e:
				self.log('ERROR : incoming blocks damaged %s'%e)
		if self.reply != None:
			self.__send_3lv_(peer,reply)
	
	#
	# handler for part of messages
	#
	def __3lv_login_key_service_handler(self,hightest_msg,body_id):
		reply = None
		peer = None
		if hightest_msg['id'] == 1: # registrate new user
			peer = hightest_msg['data']['from']
			key = encrypt.import_public_key_from_str(hightest_msg['data']['key'])
			if self.blockchain.add_new_user(hightest_msg['data']['key'],peer,self.my_name,self.private):
				reply = { # ask for blocks
					'part':conf.LOGIN_KEY_SERVICE,
					'id':2, # success ful result
					'data':{
						'from':self.my_name,
						'result':'success'
					}
				}
		if self.reply != None:
			self.__send_3lv_(peer,reply)
	#
	# handler for part of messages
	#
	def __3lv_vote_part_handler(self,hightest_msg,body_id):
		pass # FIXME
	
	#
	# handler for part of messages
	#
	def __3lv_sysctl_part_handler(self,hightest_msg,body_id):
		#
		# don't know why i create this handler ._.
		#
		pass # FIXME

	#
	# handler for body-object (b64,str)
	#
	def third_level_handler(self,_body_obj):
		body_id = None
		try:
			body_id = _body_obj['body-id']
			if self.history.exists(body_id):
				return
			self.history.add(body_id)
			body_obj = self.__3lv_norm(_body_obj['data'])
			if encrypt.verify_signature(self.blockchain.get_key(body_obj['from']),body_obj.pop('sign'),encrypt.md5(json.dumps(body_obj))):
				if body_obj['part']   == conf.ERROR_PART_ID:
					self.__3lv_error_part_handler(body_obj,body_id)
				elif body_obj['part'] == conf.USER_PART_ID:
					self.__3lv_user_part_handler(body_obj,body_id)
				elif body_obj['part'] == conf.LOGIN_KEY_SERVICE:
					self.__3lv_login_key_service_handler(body_obj,body_id)
				elif body_obj['part'] == conf.BLCKCHN_PART_ID:
					self.__3lv_blckchn_part_handler(body_obj,body_id)
				elif body_obj['part'] == conf.VOTE_PART_ID:
					self.__3lv_vote_part_handler(body_obj,body_id)
				elif body_obj['part'] == conf.SYSCTL_PART_ID:
					self.__3lv_sysctl_part_handler(body_obj,body_id)
			else:
				self.log('ERROR: incoming bobj verificetion failed')
		except Exception as e:
			self.log('ERROR: 3lv handler [%s] : %s'%(body_id,e))

	#####################################
	##              OTHERS             ##
	#####################################

	#
	#
	#
	def upd_blockchain(self,addr_info):
		host = addr_info[0]
		port = addr_info[1]
		args = sys.argv[1:]
		i = args.index('-k')
		if i <= 0:
			self.log('ERROR: upd-blckchn: expected \'-k\'')
			return
		pub = encrypt.import_public_key_from_str(open(args.pop(i)).read())
		i = args.index('-l')
		if i <= 0:
			self.log('ERROR: upd-blckchn: expected \'-l\'')
			return
		username = args.pop(i)
		conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		conn.connect((host, port))
		self.__client_socket[username] = conn
		self.__socket_clinet[str(conn)]= username
		self.blockchain.user_keys[username] = pub
		self.blockchain.servers[user_keys] = [host,port]
		self.db_update_request(username)



	#
	# handle single connection
	#
	def connection_handle(self,conn,addr):
		self.log("%s _ %s"%(conn,addr))
		self.__socket_clinet[str(conn)] = None
		SHOULD_BE_OPENED = True
		while SHOULD_BE_OPENED:
			send_box = {}
			try:
				package = self.recv_2lv(conn) # Transport-Meesage
				_from	= package['from']
				_to		= package['to']
				_data	= package['data']
				_flow	= package['flow-controll']
				
				AUTH_REG = _flow % (2**0) > 0 # auth or registrate
				SHOULD_BE_OPENED = _flow % (2**1) == 0 # close connection
				if (_flow % (2**2+2**3)) >> 2 != self.network_state:
					pass # should do s0mething

				if AUTH_REG:
					AUTH_REG = not self.auth_ot_reg(_data)
				if AUTH_REG:
					if self.__socket_clinet[str(conn)] == None:
						self.__socket_clinet[str(conn)] = _from
						self.__socket_clinet[_from] = str(conn)
					for body_object in _data:
						data = body_object['data']
						to = body_object['to']
						if to == self.my_name:
							self.third_level_handler(data)
						else:
							if to not in send_box:
								obj = {
									'from':self.my_name,
									'to':to,
									'data':[body_object]
								}
								send_box[to] = obj
							else:
								send_box[to]['data'].append(body_object)
			except Exception as e:
				self.log('ERROR: handler <%s> %s'%(addr,e))
			finally:
				flow = 0x0C & _flow
				for to in send_box:
					package = send_box[to]
					package['flow'] = _flow
					self.send_2lv(package)
		conn.close()




	#
	# main loop
	# create lots of threads and listen to socket
	#
	# Магия (идиотизм) питона в том, что есть GIL
	# Из-за которого все потоки превращаются в корутины
	# (и лишь зря занимают записи в таблицах процессов в ядре ОС)
	#
	def run(self):
		self.mainloop = True
		if '--client' in sys.argv[1:]:
			self.fork_client()
		delay = conf.delay
		try:
			self.log('Starting working')
			while self.mainloop: # MAINLOOP
				try:
					
					conn , addr = self.socket.accept()
					#
					# check for len of self.threads and kill one in case of need
					#
					_t = th.Thread( target = self.connection_handle, args = [conn,addr] )
					_t.start()
					self.threads.append(_t)
					self.check_threads()

				except socket.timeout as e: # time to reopen port
					try:
						self.socket.close()
					except Exception as e:
						self.log('Err while opennig port')
						time.sleep(delay*5)
					finally:
						self.open_port()
				except Exception as e:
					self.log('mainloop: %s'%e)
				finally:
					time.sleep(delay)
		except KeyboardInterrupt as e:
			self.log('Finish working')
			self.blockchain.save()

	#####################################################
	##                 USERS INTERFACE                 ##
	#####################################################

	#
	# 
	#
	def self_registrate(self):
		username = self.my_name
		reg_data = {
			'type':'user',
			'name':username,
			'key':encrypt.export_key(self.public).decode(),
			'address':[conf.host,conf.port]
		} 
		self.blockchain.reset()
		return self.blockchain.add_my_block(Block(n=0,data=json.dumps(reg_data),prev_hash='',hash_type='md5'),username,self.private)

	#
	# here will be forked thread for working with user
	#
	def fork_client(self):
		def client(self):
			CMDS = '''ENTER COMMAND:\n0 - exit\n1 - send msg\n'''
			while True:
				try:
					cmd = int(input(CMDS))
					if cmd == 0:
						self.mainloop = False
						return
					elif cmd == 1:
						username = input('peer-name: ')
						msg = input('message: ')
						self.send_user_message(username,msg)
					else:
						raise RuntimeError('unknown command %s'%cmd)
				except Exception as e:
					self.log('ERROR client: %s'%e)
		_t = th.Thread(target=client,args=[self])
		_t.start()
		self.threads.append(_t)