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 main() -> None: # socket object using an IPV4 address, using only raw socket access, set # ICMP protocol ping_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) packets: List[bytes] = [] while len(packets) < 1: recPacket, addr = ping_sock.recvfrom(1024) ip = headers.ip(recPacket[:20]) icmp = headers.icmp(recPacket[20:28]) print(ip) print() print(icmp) print("\n") packets.append(recPacket)
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