def tcp(address: Tuple[str, int], timeout: float) -> PORTS: """ This function is run asynchronously and listens for TCP ACK responses to the sent TCP SYN msg. """ ports: DefaultDict[str, Set[int]] = defaultdict(set) with closing( socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)) as s: s.bind(address) # bind the raw socket to the listening address time_remaining = timeout while True: time_taken = ip_utils.wait_for_socket(s, time_remaining) # wait for the socket to become readable if time_taken == -1: break else: time_remaining -= time_taken packet = s.recv(1024) # recieve the packet data tcp = headers.tcp(packet[20:40]) if tcp.flags & 2: # syn flags set ports["OPEN"].add(tcp.source) elif tcp.flags & 4: ports["CLOSED"].add(tcp.source) else: continue return ports
def udp_listener(dest_ip: str, timeout: float) -> Set[int]: """ This listener detects UDP packets from dest_ip in the given timespan, all ports that send direct replies are marked as being open. Returns a list of open ports. """ time_remaining = timeout ports: Set[int] = set() with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP) as s: while True: time_taken = ip_utils.wait_for_socket(s, time_remaining) if time_taken == -1: break else: time_remaining -= time_taken packet = s.recv(1024) ip = headers.ip(packet[:20]) udp = headers.udp(packet[20:28]) # unpack the UDP header if dest_ip == ip.source and ip.protocol == 17: ports.add(udp.src) return ports
def syn_listener(address: Tuple[str, int], timeout: float) -> List[int]: """ This function is run asynchronously and listens for TCP ACK responses to the sent TCP SYN msg. """ print(f"address: [{address}]\ntimeout: [{timeout}]") open_ports: List[int] = [] with closing( socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)) as s: s.bind(address) # bind the raw socket to the listening address time_remaining = timeout print("started listening") while True: time_taken = ip_utils.wait_for_socket(s, time_remaining) # wait for the socket to become readable if time_taken == -1: break else: time_remaining -= time_taken packet = s.recv(1024) # recieve the packet data tcp = headers.tcp(packet[20:40]) if tcp.flags == 0b00010010: # syn ack print(tcp) open_ports.append(tcp.source) # check that the header contained the TCP ACK flag and if it # did append it else: continue print("finished listening") return open_ports
def ping(ID: int, timeout: float) -> Set[Tuple[str, float, headers.ip]]: """ Takes in a process id and a timeout and returns a list of addresses which sent ICMP ECHO REPLY packets with the packed id matching ID in the time given by timeout. """ ping_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) # opens a raw socket for sending ICMP protocol packets time_remaining = timeout addresses = set() recieved_from = set() while True: time_waiting = ip_utils.wait_for_socket(ping_sock, time_remaining) # time_waiting stores the time the socket took to become readable # or returns minus one if it ran out of time if time_waiting == -1: break time_recieved = time.time() # store the time the packet was recieved recPacket, addr = ping_sock.recvfrom(1024) # recieve the packet ip = headers.ip(recPacket[:20]) # unpack the IP header into its respective components icmp = headers.icmp(recPacket[20:28]) # unpack the time from the packet. time_sent = struct.unpack("d", recPacket[28:28 + struct.calcsize("d")])[0] # unpack the value for when the packet was sent time_taken: float = time_recieved - time_sent # calculate the round trip time taken for the packet if icmp.id == ID: # if the ping was sent from this machine then add it to the list of # responses ip_address, port = addr # this is to prevent a bug where IPs were being added twice if ip_address not in recieved_from: addresses.add((ip_address, time_taken, ip)) recieved_from.add(ip_address) elif time_remaining <= 0: break else: continue # return a list of all the addesses that replied to our ICMP echo request. return addresses
def icmp_unreachable(src_ip: str, timeout: float = 2) -> int: """ This listener detects ICMP destination unreachable packets and returns the icmp code. This is later used to mark them as either close, open|filtered, filtered. 3 -> closed 0|1|2|9|10|13 -> filtered -1 -> error with arguments open|filtered means that they are either open or filtered but return nothing. """ ping_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) # open raw socket to listen for ICMP destination unrechable packets time_remaining = timeout code = -1 while True: time_waiting = ip_utils.wait_for_socket(ping_sock, time_remaining) # wait for socket to be readable if time_waiting == -1: break else: time_remaining -= time_waiting recPacket, addr = ping_sock.recvfrom(1024) # recieve the packet ip = headers.ip(recPacket[:20]) icmp = headers.icmp(recPacket[20:28]) valid_codes = [0, 1, 2, 3, 9, 10, 13] if (ip.source == src_ip and icmp.type == 3 and icmp.code in valid_codes): code = icmp.code break elif time_remaining <= 0: break else: continue ping_sock.close() return code