Example #1
0
def send_one_ping(sock: socket, dest_addr: str, icmp_id: int, seq: int, size: int):
    """Sends one ping to the given destination.

    ICMP Header (bits): type (8), code (8), checksum (16), id (16), sequence (16)
    ICMP Payload: time (double), data
    ICMP Wikipedia: https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol

    Args:
        sock: Socket.
        dest_addr: The destination address, can be an IP address or a domain name. Ex. "192.168.1.1"/"example.com"
        icmp_id: ICMP packet id, usually is same as pid.
        seq: ICMP packet sequence, usually increases from 0 in the same process.
        size: The ICMP packet payload size in bytes. Note this is only for the payload part.

    Raises:
        HostUnkown: If destination address is a domain name and cannot resolved.
    """
    try:
        dest_addr = socket.gethostbyname(dest_addr)  # Domain name will translated into IP address, and IP address leaves unchanged.
    except socket.gaierror as e:
        print("Cannot resolve {}: Unknown host".format(dest_addr))
        raise errors.HostUnknown(dest_addr) from e
    pseudo_checksum = 0  # Pseudo checksum is used to calculate the real checksum.
    icmp_header = struct.pack(ICMP_HEADER_FORMAT, IcmpType.ECHO_REQUEST, ICMP_DEFAULT_CODE, pseudo_checksum, icmp_id, seq)
    padding = (size - struct.calcsize(ICMP_TIME_FORMAT) - struct.calcsize(ICMP_HEADER_FORMAT)) * "Q"  # Using double to store current time.
    icmp_payload = struct.pack(ICMP_TIME_FORMAT, time.time()) + padding.encode()
    real_checksum = checksum(icmp_header + icmp_payload)  # Calculates the checksum on the dummy header and the icmp_payload.
    # Don't know why I need socket.htons() on real_checksum since ICMP_HEADER_FORMAT already in Network Bytes Order (big-endian)
    icmp_header = struct.pack(ICMP_HEADER_FORMAT, IcmpType.ECHO_REQUEST, ICMP_DEFAULT_CODE, socket.htons(real_checksum), icmp_id, seq)  # Put real checksum into ICMP header.
    packet = icmp_header + icmp_payload
    sock.sendto(packet, (dest_addr, 0))  # addr = (ip, port). Port is 0 respectively the OS default behavior will be used.
Example #2
0
def resolve_ip(dest_addr: str):
    """Resolves the domain name or returns the IP if IP

    Defaults to IPv4 where available, returns the ip type as string 4 or 6

    Args:
        dest_addr: IPv4, IPv6 or hostname

    Raises:
        HostUnknown: If destination address is a domain name and cannot resolved.
    """
    # Domain name will translated into IP address, and IP address leaves unchanged.
    try:
        ipv4_addresses = socket.getaddrinfo(dest_addr,
                                            None,
                                            family=socket.AF_INET)
        dest_addr = list(set(item[4][0] for item in ipv4_addresses))[0]
        return dest_addr, '4'
    except socket.gaierror:
        try:
            ipv6_addresses = socket.getaddrinfo(dest_addr,
                                                None,
                                                family=socket.AF_INET6)
            dest_addr = list(set(item[4][0] for item in ipv6_addresses))[0]
            return dest_addr, '6'
        except socket.gaierror as e:
            raise errors.HostUnknown(dest_addr) from e