def __init__(self): super(ICMP, self).__init__() self.icmp = ICMP_Proto() self.ICMP_sequence = 0 self.ICMP_identifier = int(random.random() * 65535) self.ICMP_fake_serverport = int(random.random() * 65535) self.ICMP_prefix = "XFL" self.timeout = 2.0 return
def __init__(self): super(ICMP, self).__init__() self.icmp = ICMP_Proto() self.ICMP_sequence = 0 # identifier lottery self.ICMP_identifier = int(random.random() * 65535) # serverport lottery, not like it matters self.ICMP_fake_serverport = int(random.random() * 65535) # prefix to make it easier to detect xfl packets self.ICMP_prefix = "XFL" self.timeout = 2.0 # if the recv-sent>threshold: self.TRACKING_THRESHOLD = 50 # then we cut back the difference with adjust: self.TRACKING_ADJUST = 20 return
class ICMP(Stateless_module.Stateless_module): module_name = "ICMP" module_configname = "ICMP" module_description = """ICMP type 8+0 module. Sends ping requests and responses. Just an ordinary ping tunnel.""" module_os_support = common.OS_LINUX | common.OS_MACOSX | common.OS_WINDOWS def __init__(self): super(ICMP, self).__init__() self.icmp = ICMP_Proto() self.ICMP_sequence = 0 # identifier lottery self.ICMP_identifier = int(random.random() * 65535) # serverport lottery, not like it matters self.ICMP_fake_serverport = int(random.random() * 65535) # prefix to make it easier to detect xfl packets self.ICMP_prefix = "XFL" self.timeout = 2.0 # if the recv-sent>threshold: self.TRACKING_THRESHOLD = 50 # then we cut back the difference with adjust: self.TRACKING_ADJUST = 20 return def init_client(self, control_message, additional_data): addr = additional_data[0] identifier = additional_data[1] sequence = additional_data[2] client_local = ICMP_Client() client_local.set_ICMP_received_identifier(identifier) client_local.set_ICMP_received_sequence(sequence) client_local.set_ICMP_sent_identifier(identifier) client_local.set_ICMP_sent_sequence(sequence) client_private_ip = control_message[0:4] client_public_source_ip = socket.inet_aton(addr[0]) client_public_source_port = addr[1] # If this private IP is already used, the server removes that client. # For example: client reconnect on connection reset, duplicated configs # and yes, this can be used to kick somebody off the tunnel # close client related pipes for c in self.clients: if c.get_private_ip_addr() == client_private_ip: save_to_close = c self.clients.remove(c) if c.get_pipe_r() in self.rlist: self.rlist.remove(c.get_pipe_r()) found = False for c in self.packetselector.get_clients(): if c.get_private_ip_addr() == client_private_ip: found = True self.packetselector.delete_client(c) # If client was created but not added to the PacketSelector, then the # pipes still need to be closed. This could happen when the authenti- # cation fails or gets interrupted. if not found: if self.os_type == common.OS_WINDOWS: import win32file try: win32file.CloseHandle(save_to_close.get_pipe_r()) win32file.CloseHandle(save_to_close.get_pipe_w()) except: pass else: try: save_to_close.get_pipe_r_fd().close() save_to_close.get_pipe_w_fd().close() except: pass # creating new pipes for the client pipe_r, pipe_w = os.pipe() client_local.set_pipes_fdnum(pipe_r, pipe_w) client_local.set_pipes_fd(os.fdopen(pipe_r, "r"), os.fdopen(pipe_w, "w")) # set connection related things and authenticated to True client_local.set_public_ip_addr(client_public_source_ip) client_local.set_public_src_port(client_public_source_port) client_local.set_private_ip_addr(client_private_ip) client_local.get_encryption().set_module(self.encryption.get_module()) self.encryption = client_local.get_encryption() if self.encryption.get_module().get_step_count(): # add encryption steps self.merge_cmh(self.encryption.get_module().get_cmh_struct()) if self.authentication.get_step_count(): # add authentication steps self.merge_cmh(self.authentication.get_cmh_struct()) client_local.set_initiated(True) self.clients.append(client_local) return def lookup_client_pub(self, additional_data): addr = additional_data[0] identifier = additional_data[1] client_public_ip = socket.inet_aton(addr[0]) for c in self.clients: if (c.get_public_ip_addr() == client_public_ip) and (c.get_ICMP_received_identifier() == identifier): return c return None def post_authentication_server(self, control_message, additional_data): addr = additional_data[0] identifier = additional_data[1] c = self.lookup_client_pub((addr, identifier)) if c.get_initiated(): c.set_authenticated(True) self.packetselector.add_client(c) if c.get_pipe_r() not in self.rlist: self.rlist.append(c.get_pipe_r()) return True return False def remove_initiated_client(self, control_message, additional_data): addr = additional_data[0] identifier = additional_data[1] c = self.lookup_client_pub((addr, identifier)) if c: self.packetselector.delete_client(c) if c.get_authenticated(): self.rlist.remove(c.get_pipe_r()) self.clients.remove(c) return def communication_initialization(self): self.clients = [] if self.serverorclient: if self.os_type == common.OS_LINUX: ps = subprocess.Popen( ["cat", "/proc/sys/net/ipv4/icmp_echo_ignore_all"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print( "Error: deleting default route: {0}".format(stderr), -1) sys.exit(-1) self.orig_ieia_value = stdout[0:1] os.system("echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all") if self.serverorclient: self.ICMP_send = self.icmp.ICMP_ECHO_RESPONSE else: self.ICMP_send = self.icmp.ICMP_ECHO_REQUEST return def modify_additional_data(self, additional_data, serverorclient): if serverorclient: c = self.lookup_client_pub(additional_data) if c: c.set_ICMP_sent_sequence(additional_data[2]) return additional_data else: # increment sequence in additional data self.ICMP_sequence += 1 return (additional_data[0], additional_data[1], self.ICMP_sequence, additional_data[3]) # check request: generating a challenge and sending it to the server # in case the answer is that is expected, the targer is a valid server def do_check(self): message, self.check_result = self.checks.check_default_generate_challenge( ) self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_CHECK + message, (self.server_tuple, self.ICMP_identifier, 0, 0)) return # start talking to the server # do authentication or encryption first def do_hello(self): # TODO: maybe change this later to push some more info, not just the # private IP message = socket.inet_aton(self.config.get("Global", "clientip")) self.send( common.CONTROL_CHANNEL_BYTE, common.CONTROL_INIT + message, (self.server_tuple, self.ICMP_identifier, self.ICMP_sequence, 0)) # Polite signal towards the server to tell that the client is leaving # Can be spoofed? if there is no encryption. Who cares? def do_logoff(self): self.send( common.CONTROL_CHANNEL_BYTE, common.CONTROL_LOGOFF, (self.server_tuple, self.ICMP_identifier, self.ICMP_sequence, 0)) return def do_dummy_packet(self, identifier, sequence): self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_DUMMY_PACKET, (self.server_tuple, identifier, sequence, 0)) return def send(self, channel_type, message, additional_data): addr = additional_data[0] identifier = additional_data[1] sequence = additional_data[2] queue_length = additional_data[3] if queue_length < 256: ql = chr(queue_length) else: ql = chr(255) if channel_type == common.CONTROL_CHANNEL_BYTE: transformed_message = self.transform( self.get_client_encryption(additional_data), ql + common.CONTROL_CHANNEL_BYTE + message, 1) else: transformed_message = self.transform( self.get_client_encryption(additional_data), ql + common.DATA_CHANNEL_BYTE + message, 1) common.internal_print( "ICMP sent: {0} seq: {1} id: {2}".format(len(transformed_message), sequence, identifier), 0, self.verbosity, common.DEBUG) packet = self.icmp.create_packet( self.ICMP_send, identifier, sequence, self.ICMP_prefix + struct.pack(">H", len(transformed_message)) + transformed_message) # WORKAROUND?! # Windows: It looks like when the buffer fills up the OS does not do # congestion control, instead throws and exception/returns with # WSAEWOULDBLOCK which means that we need to try it again later. # So we sleep 100ms and hope that the buffer has more space for us. # If it does then it sends the data, otherwise tries it in an infinite # loop... while True: try: return self.comms_socket.sendto(packet, addr) except socket.error as se: if se.args[0] == 10035: # WSAEWOULDBLOCK time.sleep(0.1) pass else: raise def recv(self): message, addr = self.comms_socket.recvfrom(1508) identifier = struct.unpack("<H", message[24:26])[0] sequence = struct.unpack(">H", message[26:28])[0] if message[28:28 + len(self.ICMP_prefix)] != self.ICMP_prefix: return ("", None, None, None, None) message = message[28 + len(self.ICMP_prefix):] length = struct.unpack(">H", message[0:2])[0] if (length + 2 != len(message)): common.internal_print( "Error length mismatch {0} {1}".format(length, len(message)), -1) return ("", None, None, None, None) message = self.transform( self.get_client_encryption((addr, identifier, 0, 0)), message[2:length + 2], 0) queue_length = struct.unpack(">B", message[0:1])[0] common.internal_print( "ICMP read: {0} seq: {1} id: {2}".format(length, sequence, identifier), 0, self.verbosity, common.DEBUG) return message[1:], addr, identifier, sequence, queue_length def communication_win(self, is_check): import win32event import win32file import win32api import pywintypes import winerror # event for the socket hEvent_sock = win32event.CreateEvent(None, 0, 0, None) win32file.WSAEventSelect(self.comms_socket, hEvent_sock, win32file.FD_READ) # descriptor list self.rlist = [self.comms_socket] # overlapped list self.olist = [0] # event list self.elist = [hEvent_sock] # message buffer list self.mlist = [0] # id of the read object - put in this if it was read self.ulist = [] if not self.serverorclient and self.tunnel: # client mode # objects created for the tunnel and put in the corresponding # lists hEvent_pipe = win32event.CreateEvent( None, 0, 0, None) # for reading from the pipe overlapped_pipe = pywintypes.OVERLAPPED() overlapped_pipe.hEvent = hEvent_pipe message_buffer = win32file.AllocateReadBuffer(4096) self.rlist.append(self.tunnel) self.olist.append(overlapped_pipe) self.elist.append(hEvent_pipe) self.mlist.append(message_buffer) self.ulist.append(1) while not self._stop: try: if not self.tunnel: # check or server mode without client only with socket #message, addr = self.comms_socket.recvfrom(1508) rc = win32event.WaitForSingleObject( hEvent_sock, int(self.timeout * 1000)) if rc == winerror.WAIT_TIMEOUT: # timed out, just rerun and wait continue else: if self.ulist: # there is somebody waiting to be read for idx in self.ulist: # issueing ReadFile on all not yet read mailslots/tunnel hr, _ = win32file.ReadFile(self.rlist[idx], self.mlist[idx], self.olist[idx]) if (hr != 0) and (hr != winerror.ERROR_IO_PENDING): common.internal_print( "UDP ReadFile failed: {0}".format(hr), -1) raise self.ulist = [] # waiting to get some data somewhere rc = win32event.WaitForMultipleObjects( self.elist, 0, int(self.timeout * 1000)) if rc == winerror.WAIT_TIMEOUT: # timed out, just rerun and wait continue if rc < 0x80: # STATUS_ABANDONED_WAIT_0 if rc > 0: # the tunnel or one of the mailslots got signalled self.ulist.append(rc) if (self.olist[rc].InternalHigh < 4) or (self.mlist[rc][0:1] != "\x45"): #Only care about IPv4 continue readytogo = self.mlist[rc][0:self.olist[rc]. InternalHigh] if self.serverorclient: c = self.lookup_client_priv(readytogo) if c: # if the differece between the received and set sequences too big # some routers/firewalls just drop older sequences. If it gets # too big, we just drop the older ones and use the latest X packet # this helps on stabality. if (c.get_ICMP_received_sequence() - c.get_ICMP_sent_sequence() ) >= self.TRACKING_THRESHOLD: c.set_ICMP_sent_sequence( c.get_ICMP_received_sequence() - self.TRACKING_ADJUST) # get client related values: identifier and sequence number identifier = c.get_ICMP_sent_identifier() sequence = c.get_ICMP_sent_sequence() # queueing every packet first c.queue_put(readytogo) # are there any packets to answer? if (c.get_ICMP_received_sequence() - sequence) == 0: continue else: request_num = 0 # if there is less packet than that we have in the queue # then we cap the outgoing packet number if (c.get_ICMP_received_sequence() - sequence) < (c.queue_length()): number_to_get = ( c.get_ICMP_received_sequence() - sequence) else: # send all packets from the queue number_to_get = c.queue_length() for i in range(0, number_to_get): # get first packet readytogo = c.queue_get() # is it he last one we are sending now? if i == (number_to_get - 1): # if the last one and there is more in the queue # then we ask for dummy packets request_num = c.queue_length() # go packets go! self.send(common.DATA_CHANNEL_BYTE, readytogo, ((socket.inet_ntoa( c.get_public_ip_addr()), c.get_public_src_port()), identifier, sequence + i + 1, request_num)) sequence = (sequence + i + 1) % 65536 c.set_ICMP_sent_sequence(sequence) else: # there is no client with that IP common.internal_print( "Client not found, strange?!", 0, self.verbosity, common.DEBUG) continue else: if self.authenticated: # whatever we have from the tunnel, just encapsulate it # and send it out self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.send( common.DATA_CHANNEL_BYTE, readytogo, (self.server_tuple, self.ICMP_identifier, self.ICMP_sequence, 0)) #?? else: common.internal_print( "Spoofed packets, strange?!", 0, self.verbosity, common.DEBUG) continue if rc == 0: # socket got signalled message, addr, identifier, sequence, queue_length = self.recv( ) if len(message) == 0: continue c = None if self.serverorclient: self.authenticated = False c = self.lookup_client_pub((addr, 0)) if c: c.set_ICMP_received_identifier(identifier) # packets does not arrive in order sometime # if higher sequence arrived already, then we # do not modify # 16bit integer MAX could be a bit tricky, a # threshold had to be introduced to make it # fail safe. Hacky but should work. ICMP_THRESHOLD = 100 if (sequence > c.get_ICMP_received_sequence() ) or ((sequence < ICMP_THRESHOLD) and ((sequence + 65536) > c.get_ICMP_received_sequence()) and (c.get_ICMP_received_sequence() > ICMP_THRESHOLD)): c.set_ICMP_received_sequence(sequence) else: if queue_length: common.internal_print( "sending {0} dummy packets".format( queue_length), 0, self.verbosity, common.DEBUG) for i in range(queue_length + 10): self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet( self.ICMP_identifier, self.ICMP_sequence) if common.is_control_channel(message[0:1]): if self.controlchannel.handle_control_messages( self, message[len(common.CONTROL_CHANNEL_BYTE):], (addr, identifier, sequence, 0)): continue else: self.stop() break if c: self.authenticated = c.get_authenticated() if self.authenticated: try: self.packet_writer( message[len(common.CONTROL_CHANNEL_BYTE):]) except OSError as e: print(e) except win32api.error as e: common.internal_print("UDP Exception: {0}".format(e), -1) except socket.error as se: if se.args[0] == 10054: # port is unreachable common.internal_print( "Server's port is unreachable: {0}".format(se), -1) self._stop = True return True def communication_unix(self, is_check): sequence = 0 identifier = 0 self.rlist = [self.comms_socket] if not self.serverorclient and self.tunnel: self.rlist = [self.tunnel, self.comms_socket] wlist = [] xlist = [] while not self._stop: try: readable, writable, exceptional = select.select( self.rlist, wlist, xlist, self.timeout) except select.error, e: common.internal_print("select.error: %r".format(e), -1) break try: if not readable: if is_check: raise socket.timeout if not self.serverorclient: if self.authenticated: self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet(self.ICMP_identifier, self.ICMP_sequence) common.internal_print("Keep alive sent", 0, self.verbosity, common.DEBUG) continue for s in readable: if (s in self.rlist) and not (s is self.comms_socket): message = self.packet_reader(s, True, self.serverorclient) while True: if (len(message) < 4) or (message[0:1] != "\x45" ): #Only care about IPv4 break packetlen = struct.unpack( ">H", message[2:4])[0] # IP Total length if packetlen > len(message): message += self.packet_reader( s, False, self.serverorclient) readytogo = message[0:packetlen] message = message[packetlen:] if self.serverorclient: c = self.lookup_client_priv(readytogo) if c: # if the differece between the received and set sequences too big # some routers/firewalls just drop older sequences. If it gets # too big, we just drop the older ones and use the latest X packet # this helps on stabality. if (c.get_ICMP_received_sequence() - c.get_ICMP_sent_sequence() ) >= self.TRACKING_THRESHOLD: c.set_ICMP_sent_sequence( c.get_ICMP_received_sequence() - self.TRACKING_ADJUST) # get client related values: identifier and sequence number identifier = c.get_ICMP_sent_identifier() sequence = c.get_ICMP_sent_sequence() # queueing every packet first c.queue_put(readytogo) # are there any packets to answer? if (c.get_ICMP_received_sequence() - sequence) == 0: continue else: request_num = 0 # if there is less packet than that we have in the queue # then we cap the outgoing packet number if (c.get_ICMP_received_sequence() - sequence) < (c.queue_length()): number_to_get = ( c.get_ICMP_received_sequence() - sequence) else: # send all packets from the queue number_to_get = c.queue_length() for i in range(0, number_to_get): # get first packet readytogo = c.queue_get() # is it he last one we are sending now? if i == (number_to_get - 1): # if the last one and there is more in the queue # then we ask for dummy packets request_num = c.queue_length() # go packets go! self.send( common.DATA_CHANNEL_BYTE, readytogo, ((socket.inet_ntoa( c.get_public_ip_addr()), c.get_public_src_port()), identifier, sequence + i + 1, request_num)) sequence = (sequence + i + 1) % 65536 c.set_ICMP_sent_sequence(sequence) else: # there is no client with that IP common.internal_print( "Client not found, strange?!", 0, self.verbosity, common.DEBUG) continue else: if self.authenticated: # whatever we have from the tunnel, just encapsulate it # and send it out self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.send(common.DATA_CHANNEL_BYTE, readytogo, (self.server_tuple, self.ICMP_identifier, self.ICMP_sequence, 0)) #?? else: common.internal_print( "Spoofed packets, strange?!", 0, self.verbosity, common.DEBUG) continue if s is self.comms_socket: message, addr, identifier, sequence, queue_length = self.recv( ) if len(message) == 0: continue c = None if self.serverorclient: self.authenticated = False c = self.lookup_client_pub((addr, identifier)) if c: c.set_ICMP_received_identifier(identifier) # packets does not arrive in order sometime # if higher sequence arrived already, then we # do not modify # 16bit integer MAX could be a bit tricky, a # threshold had to be introduced to make it # fail safe. Hacky but should work. ICMP_THRESHOLD = 100 if (sequence > c.get_ICMP_received_sequence() ) or ((sequence < ICMP_THRESHOLD) and ((sequence + 65536) > c.get_ICMP_received_sequence()) and (c.get_ICMP_received_sequence() > ICMP_THRESHOLD)): c.set_ICMP_received_sequence(sequence) else: if queue_length: common.internal_print( "sending {0} dummy packets".format( queue_length), 0, self.verbosity, common.DEBUG) for i in range(queue_length + 10): self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet( self.ICMP_identifier, self.ICMP_sequence) if common.is_control_channel(message[0:1]): if self.controlchannel.handle_control_messages( self, message[len(common.CONTROL_CHANNEL_BYTE):], (addr, identifier, sequence, 0)): continue else: self.stop() break if c: self.authenticated = c.get_authenticated() if self.authenticated: try: self.packet_writer( message[len(common.CONTROL_CHANNEL_BYTE):]) except OSError as e: print(e) except (socket.error, OSError): raise if self.serverorclient: self.comms_socket.close() break except: print("another error") raise return
class ICMP(Stateless_module.Stateless_module): module_name = "ICMP" module_configname = "ICMP" module_description = """... ICMP ... """ def __init__(self): super(ICMP, self).__init__() self.icmp = ICMP_Proto() self.ICMP_sequence = 0 self.ICMP_identifier = int(random.random() * 65535) self.ICMP_fake_serverport = int(random.random() * 65535) self.ICMP_prefix = "XFL" self.timeout = 2.0 return def setup_authenticated_client(self, control_message, additional_data): addr = additional_data[0] identifier = additional_data[1] sequence = additional_data[2] client_local = ICMP_Client() common.init_client_stateless(control_message, addr, client_local, self.packetselector, self.clients) self.clients.append(client_local) self.packetselector.add_client(client_local) if client_local.get_pipe_r() not in self.rlist: self.rlist.append(client_local.get_pipe_r()) client_local.set_ICMP_received_identifier(identifier) client_local.set_ICMP_received_sequence(sequence) client_local.set_ICMP_sent_identifier(identifier) client_local.set_ICMP_sent_sequence(sequence) self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_AUTH_OK, additional_data) return def remove_authenticated_client(self, additional_data): addr = additional_data[0] # ICMP specific c = common.lookup_client_pub(self.clients, addr) if c: self.packetselector.delete_client(c) self.rlist.remove(c.get_pipe_r()) common.delete_client_stateless(self.clients, c) return def communication_initialization(self): self.clients = [] os.system("echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all") #??? if self.serverorclient: self.ICMP_send = self.icmp.ICMP_ECHO_RESPONSE else: self.ICMP_send = self.icmp.ICMP_ECHO_REQUEST return def do_check(self): message, self.check_result = common.check_gen() self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_CHECK + message, (self.server_tuple, self.ICMP_identifier, 0, 0)) #?? return def do_auth(self): message = common.auth_first_step(self.config.get("Global", "clientip"), self.comms_socket) self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_AUTH + message, (self.server_tuple, self.ICMP_identifier, 0, 0)) #?? return def do_logoff(self): self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_LOGOFF, (self.server_tuple, self.ICMP_identifier, 0, 0)) #?? return def do_dummy_packet(self, identifier, sequence): self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_DUMMY_PACKET, (self.server_tuple, identifier, sequence, 0)) #?? return def send(self, type, message, additional_data): addr = additional_data[0] identifier = additional_data[1] sequence = additional_data[2] queue_length = additional_data[3] if queue_length < 256: ql = chr(queue_length) else: ql = chr(255) if type == common.CONTROL_CHANNEL_BYTE: transformed_message = self.transform( ql + common.CONTROL_CHANNEL_BYTE + message, 1) else: transformed_message = self.transform( ql + common.DATA_CHANNEL_BYTE + message, 1) common.internal_print( "ICMP sent: {0} seq: {1} id: {2}".format(len(transformed_message), sequence, identifier), 0, self.verbosity, common.DEBUG) return self.comms_socket.sendto( self.icmp.create_packet( self.ICMP_send, identifier, sequence, self.ICMP_prefix + struct.pack(">H", len(transformed_message)) + transformed_message), addr) def recv(self): message, addr = self.comms_socket.recvfrom(1508) identifier = struct.unpack("<H", message[24:26])[0] #sequence = struct.unpack("<H", message[26:28])[0] sequence = struct.unpack(">H", message[26:28])[0] if message[28:28 + len(self.ICMP_prefix)] != self.ICMP_prefix: return ("", None, None, None, None) message = message[28 + len(self.ICMP_prefix):] length, queue_length = struct.unpack(">HB", message[0:3]) length += 2 if (length != len(message)): common.internal_print( "Error length mismatch {0} {1}".format(length, len(message)), -1) return ("", None, None, None, None) common.internal_print( "ICMP read: {0} seq: {1} id: {2}".format( len(message) - 2, sequence, identifier), 0, self.verbosity, common.DEBUG) return message[3:], addr, identifier, sequence, queue_length def communication(self, is_check): sequence = 0 identifier = 0 self.rlist = [self.comms_socket] if not self.serverorclient and self.tunnel: self.rlist = [self.tunnel, self.comms_socket] wlist = [] xlist = [] while not self._stop: try: readable, writable, exceptional = select.select( self.rlist, wlist, xlist, self.timeout) except select.error, e: print e break try: if not readable: if is_check: raise socket.timeout if not self.serverorclient: if self.authenticated: self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet(self.ICMP_identifier, self.ICMP_sequence) common.internal_print("DEBUG: Keep alive sent", 0, self.verbosity, common.DEBUG) continue for s in readable: if (s in self.rlist) and not (s is self.comms_socket): message = os.read(s, 4096) while True: if (len(message) < 4) or (message[0:1] != "\x45" ): #Only care about IPv4 break packetlen = struct.unpack( ">H", message[2:4])[0] # IP Total length if packetlen > len(message): message += os.read(s, 4096) readytogo = message[0:packetlen] message = message[packetlen:] if self.serverorclient: c = common.lookup_client_priv( readytogo, self.clients) if c: identifier = c.get_ICMP_sent_identifier() sequence = c.get_ICMP_sent_sequence() if (c.get_ICMP_received_sequence() - sequence) == 0: c.queue_put(readytogo) continue else: if c.queue_length(): c.queue_put(readytogo) i = 0 if (c.get_ICMP_received_sequence() - sequence) < ( c.queue_length()): number_to_get = ( c. get_ICMP_received_sequence( ) - sequence) else: number_to_get = c.queue_length( ) for i in range(number_to_get - 1): readytogo = c.queue_get() self.send( common.DATA_CHANNEL_BYTE, readytogo, ((socket.inet_ntoa( c.get_public_ip_addr( )), c.get_public_src_port()), identifier, (sequence + i + 1), 0)) #?? readytogo = c.queue_get() sequence += i sequence = (sequence + 1) % 65536 c.set_ICMP_sent_sequence(sequence) self.send(common.DATA_CHANNEL_BYTE, readytogo, ((socket.inet_ntoa( c.get_public_ip_addr()), c.get_public_src_port()), identifier, sequence, c.queue_length())) #?? else: common.internal_print( "Client not found, strange?!", 0, self.verbosity, common.DEBUG) continue else: if self.authenticated: self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.send(common.DATA_CHANNEL_BYTE, readytogo, (self.server_tuple, self.ICMP_identifier, self.ICMP_sequence, 0)) #?? sequence = self.ICMP_sequence # del this line identifier = self.ICMP_identifier # del this line else: common.internal_print( "Spoofed packets, strange?!", 0, self.verbosity, common.DEBUG) continue if s is self.comms_socket: message, addr, identifier, sequence, queue_length = self.recv( ) if len(message) == 0: continue c = None if self.serverorclient: self.authenticated = False c = common.lookup_client_pub(self.clients, addr) if c: c.set_ICMP_received_identifier(identifier) # packets does not arrive in order sometime # if higher sequence arrived already, then we # do not modify # 16bit integer MAX could be a bit tricky, a # threshold had to be introduced to make it # fail safe. Hacky but should work. ICMP_THRESHOLD = 100 if (sequence > c.get_ICMP_received_sequence() ) or ((sequence < ICMP_THRESHOLD) and ((sequence + 65536) > c.get_ICMP_received_sequence()) and (c.get_ICMP_received_sequence() > ICMP_THRESHOLD)): c.set_ICMP_received_sequence(sequence) else: if queue_length: common.internal_print( "sending {0} dummy packets".format( queue_length), 0, self.verbosity, common.DEBUG) for i in range(queue_length + 10): self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet( self.ICMP_identifier, self.ICMP_sequence) if message[0:len(common.CONTROL_CHANNEL_BYTE )] == common.CONTROL_CHANNEL_BYTE: if self.controlchannel.handle_control_messages( self, message[len(common.CONTROL_CHANNEL_BYTE):], (addr, identifier, sequence, 0)): continue else: self.stop() break if c: self.authenticated = c.get_authenticated() if self.authenticated: try: os.write( self.tunnel, message[len(common.CONTROL_CHANNEL_BYTE):]) except OSError as e: print e except (socket.error, OSError): raise if self.serverorclient: self.comms_socket.close() break except: print "another error" raise return
class ICMP(Stateless_module.Stateless_module): module_name = "ICMP" module_configname = "ICMP" module_description = """... ICMP ... """ module_os_support = common.OS_LINUX | common.OS_MACOSX def __init__(self): super(ICMP, self).__init__() self.icmp = ICMP_Proto() self.ICMP_sequence = 0 # identifier lottery self.ICMP_identifier = int(random.random() * 65535) # serverport lottery, not like it matters self.ICMP_fake_serverport = int(random.random() * 65535) # prefix to make it easier to detect xfl packets self.ICMP_prefix = "XFL" self.timeout = 2.0 # if the recv-sent>threshold: self.TRACKING_THRESHOLD = 50 # then we cut back the difference with adjust: self.TRACKING_ADJUST = 20 return def setup_authenticated_client(self, control_message, additional_data): addr = additional_data[0] identifier = additional_data[1] sequence = additional_data[2] client_local = ICMP_Client() common.init_client_stateless(control_message, addr, client_local, self.packetselector, self.clients) self.clients.append(client_local) self.packetselector.add_client(client_local) if client_local.get_pipe_r() not in self.rlist: self.rlist.append(client_local.get_pipe_r()) client_local.set_ICMP_received_identifier(identifier) client_local.set_ICMP_received_sequence(sequence) client_local.set_ICMP_sent_identifier(identifier) client_local.set_ICMP_sent_sequence(sequence) self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_AUTH_OK, additional_data) return def remove_authenticated_client(self, additional_data): addr = additional_data[0] # ICMP specific c = common.lookup_client_pub(self.clients, addr) if c: self.packetselector.delete_client(c) self.rlist.remove(c.get_pipe_r()) common.delete_client_stateless(self.clients, c) return def lookup_client_pub(self, clients, addr, identifier): client_public_ip = socket.inet_aton(addr[0]) for c in clients: if (c.get_public_ip_addr() == client_public_ip) and (c.get_ICMP_received_identifier() == identifier): return c return None def communication_initialization(self): self.clients = [] if self.serverorclient: if self.os_type == common.OS_LINUX: ps = subprocess.Popen(["cat", "/proc/sys/net/ipv4/icmp_echo_ignore_all"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = ps.communicate() if stderr: common.internal_print("Error: deleting default route: {0}".format(stderr), -1) sys.exit(-1) self.orig_ieia_value = stdout[0:1] os.system("echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all") if self.serverorclient: self.ICMP_send = self.icmp.ICMP_ECHO_RESPONSE else: self.ICMP_send = self.icmp.ICMP_ECHO_REQUEST return def do_check(self): message, self.check_result = self.checks.check_default_generate_challenge() self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_CHECK+message, (self.server_tuple, self.ICMP_identifier, 0, 0)) #?? return def do_auth(self): message = self.auth_module.send_details(self.config.get("Global", "clientip")) self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_AUTH+message, (self.server_tuple, self.ICMP_identifier, 0, 0)) #?? return def do_logoff(self): self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_LOGOFF, (self.server_tuple, self.ICMP_identifier, 0, 0)) #?? return def do_dummy_packet(self, identifier, sequence): self.send(common.CONTROL_CHANNEL_BYTE, common.CONTROL_DUMMY_PACKET, (self.server_tuple, identifier, sequence, 0)) return def send(self, channel_type, message, additional_data): addr = additional_data[0] identifier = additional_data[1] sequence = additional_data[2] queue_length = additional_data[3] if queue_length < 256: ql = chr(queue_length) else: ql = chr(255) if channel_type == common.CONTROL_CHANNEL_BYTE: transformed_message = self.transform(ql+common.CONTROL_CHANNEL_BYTE+message, 1) else: transformed_message = self.transform(ql+common.DATA_CHANNEL_BYTE+message, 1) common.internal_print("ICMP sent: {0} seq: {1} id: {2}".format(len(transformed_message), sequence, identifier), 0, self.verbosity, common.DEBUG) return self.comms_socket.sendto( self.icmp.create_packet(self.ICMP_send, identifier, sequence, self.ICMP_prefix+struct.pack(">H", len(transformed_message))+transformed_message), addr) def recv(self): # self.transform is missing, TODO message, addr = self.comms_socket.recvfrom(1508) identifier = struct.unpack("<H", message[24:26])[0] #sequence = struct.unpack("<H", message[26:28])[0] sequence = struct.unpack(">H", message[26:28])[0] if message[28:28+len(self.ICMP_prefix)] != self.ICMP_prefix: return ("", None, None, None, None) message = message[28+len(self.ICMP_prefix):] length, queue_length = struct.unpack(">HB", message[0:3]) length += 2 if (length != len(message)): common.internal_print("Error length mismatch {0} {1}".format(length, len(message)), -1) return ("", None, None, None, None) common.internal_print("ICMP read: {0} seq: {1} id: {2}".format(len(message)-2, sequence, identifier), 0, self.verbosity, common.DEBUG) return message[3:], addr, identifier, sequence, queue_length def communication_unix(self, is_check): sequence = 0 identifier = 0 self.rlist = [self.comms_socket] if not self.serverorclient and self.tunnel: self.rlist = [self.tunnel, self.comms_socket] wlist = [] xlist = [] while not self._stop: try: readable, writable, exceptional = select.select(self.rlist, wlist, xlist, self.timeout) except select.error, e: print(e) break try: if not readable: if is_check: raise socket.timeout if not self.serverorclient: if self.authenticated: self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet(self.ICMP_identifier, self.ICMP_sequence) common.internal_print("Keep alive sent", 0, self.verbosity, common.DEBUG) continue for s in readable: if (s in self.rlist) and not (s is self.comms_socket): message = self.packet_reader(s, True, self.serverorclient) while True: if (len(message) < 4) or (message[0:1] != "\x45"): #Only care about IPv4 break packetlen = struct.unpack(">H", message[2:4])[0] # IP Total length if packetlen > len(message): message += self.packet_reader(s, False, self.serverorclient) readytogo = message[0:packetlen] message = message[packetlen:] if self.serverorclient: c = common.lookup_client_priv(self.clients, readytogo) if c: # if the differece between the received and set sequences too big # some routers/firewalls just drop older sequences. If it gets # too big, we just drop the older ones and use the latest X packet # this helps on stabality. if (c.get_ICMP_received_sequence()-c.get_ICMP_sent_sequence()) >= self.TRACKING_THRESHOLD: c.set_ICMP_sent_sequence(c.get_ICMP_received_sequence()-self.TRACKING_ADJUST) # get client related values: identifier and sequence number identifier = c.get_ICMP_sent_identifier() sequence = c.get_ICMP_sent_sequence() # queueing every packet first c.queue_put(readytogo) # are there any packets to answer? if (c.get_ICMP_received_sequence() - sequence) == 0: continue else: # if there is less packet than that we have in the queue # then we cap the outgoing packet number if (c.get_ICMP_received_sequence() - sequence) < (c.queue_length()): number_to_get = (c.get_ICMP_received_sequence() - sequence) else: # send all packets from the queue number_to_get = c.queue_length() request_num = 0 for i in range(0, number_to_get): # get first packet readytogo = c.queue_get() # is it he last one we are sending now? if i == (number_to_get - 1): # if the last one and there is more in the queue # then we ask for dummy packets request_num = c.queue_length() # go packets go! self.send(common.DATA_CHANNEL_BYTE, readytogo, ((socket.inet_ntoa(c.get_public_ip_addr()), c.get_public_src_port()), identifier, sequence + i + 1, request_num)) sequence = (sequence + i + 1) % 65536 c.set_ICMP_sent_sequence(sequence) else: # there is no client with that IP common.internal_print("Client not found, strange?!", 0, self.verbosity, common.DEBUG) continue else: if self.authenticated: # whatever we have from the tunnel, just encapsulate it # and send it out self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.send(common.DATA_CHANNEL_BYTE, readytogo, (self.server_tuple, self.ICMP_identifier, self.ICMP_sequence, 0)) #?? else: common.internal_print("Spoofed packets, strange?!", 0, self.verbosity, common.DEBUG) continue if s is self.comms_socket: message, addr, identifier, sequence, queue_length = self.recv() if len(message) == 0: continue c = None if self.serverorclient: self.authenticated = False c = self.lookup_client_pub(self.clients, addr, identifier) if c: c.set_ICMP_received_identifier(identifier) # packets does not arrive in order sometime # if higher sequence arrived already, then we # do not modify # 16bit integer MAX could be a bit tricky, a # threshold had to be introduced to make it # fail safe. Hacky but should work. ICMP_THRESHOLD = 100 if (sequence > c.get_ICMP_received_sequence()) or ((sequence < ICMP_THRESHOLD) and ((sequence + 65536)>c.get_ICMP_received_sequence()) and (c.get_ICMP_received_sequence()>ICMP_THRESHOLD)): c.set_ICMP_received_sequence(sequence) else: if queue_length: common.internal_print("sending {0} dummy packets".format(queue_length), 0, self.verbosity, common.DEBUG) for i in range(queue_length+10): self.ICMP_sequence = (self.ICMP_sequence + 1) % 65536 self.do_dummy_packet(self.ICMP_identifier, self.ICMP_sequence) if common.is_control_channel(message[0:1]): if self.controlchannel.handle_control_messages(self, message[len(common.CONTROL_CHANNEL_BYTE):], (addr, identifier, sequence, 0)): continue else: self.stop() break if c: self.authenticated = c.get_authenticated() if self.authenticated: try: self.packet_writer(message[len(common.CONTROL_CHANNEL_BYTE):]) except OSError as e: print(e) except (socket.error, OSError): raise if self.serverorclient: self.comms_socket.close() break except: print("another error") raise return