|
| 1 | +import os, sys, socket, struct, select, time |
| 2 | + |
| 3 | +# From /usr/include/linux/icmp.h; your milage may vary. |
| 4 | +ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris. |
| 5 | + |
| 6 | + |
| 7 | +def checksum(source_string): |
| 8 | + """ |
| 9 | + I'm not too confident that this is right but testing seems |
| 10 | + to suggest that it gives the same answers as in_cksum in ping.c |
| 11 | + """ |
| 12 | + sum = 0 |
| 13 | + countTo = (len(source_string)/2)*2 |
| 14 | + count = 0 |
| 15 | + while count<countTo: |
| 16 | + thisVal = ord(source_string[count + 1])*256 + ord(source_string[count]) |
| 17 | + sum = sum + thisVal |
| 18 | + sum = sum & 0xffffffff # Necessary? |
| 19 | + count = count + 2 |
| 20 | + |
| 21 | + if countTo<len(source_string): |
| 22 | + sum = sum + ord(source_string[len(source_string) - 1]) |
| 23 | + sum = sum & 0xffffffff # Necessary? |
| 24 | + |
| 25 | + sum = (sum >> 16) + (sum & 0xffff) |
| 26 | + sum = sum + (sum >> 16) |
| 27 | + answer = ~sum |
| 28 | + answer = answer & 0xffff |
| 29 | + |
| 30 | + # Swap bytes. Bugger me if I know why. |
| 31 | + answer = answer >> 8 | (answer << 8 & 0xff00) |
| 32 | + |
| 33 | + return answer |
| 34 | + |
| 35 | + |
| 36 | +def receive_one_ping(my_socket, ID, timeout): |
| 37 | + """ |
| 38 | + receive the ping from the socket. |
| 39 | + """ |
| 40 | + timeLeft = timeout |
| 41 | + while True: |
| 42 | + startedSelect = time.time() |
| 43 | + whatReady = select.select([my_socket], [], [], timeLeft) |
| 44 | + howLongInSelect = (time.time() - startedSelect) |
| 45 | + if whatReady[0] == []: # Timeout |
| 46 | + return |
| 47 | + |
| 48 | + timeReceived = time.time() |
| 49 | + recPacket, addr = my_socket.recvfrom(1024) |
| 50 | + icmpHeader = recPacket[20:28] |
| 51 | + type, code, checksum, packetID, sequence = struct.unpack( |
| 52 | + "bbHHh", icmpHeader |
| 53 | + ) |
| 54 | + if packetID == ID: |
| 55 | + bytesInDouble = struct.calcsize("d") |
| 56 | + timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0] |
| 57 | + return timeReceived - timeSent |
| 58 | + |
| 59 | + timeLeft = timeLeft - howLongInSelect |
| 60 | + if timeLeft <= 0: |
| 61 | + return |
| 62 | + |
| 63 | + |
| 64 | +def send_one_ping(my_socket, dest_addr, ID): |
| 65 | + """ |
| 66 | + Send one ping to the given >dest_addr<. |
| 67 | + """ |
| 68 | + dest_addr = socket.gethostbyname(dest_addr) |
| 69 | + |
| 70 | + # Header is type (8), code (8), checksum (16), id (16), sequence (16) |
| 71 | + my_checksum = 0 |
| 72 | + |
| 73 | + # Make a dummy heder with a 0 checksum. |
| 74 | + header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1) |
| 75 | + bytesInDouble = struct.calcsize("d") |
| 76 | + data = (192 - bytesInDouble) * "Q" |
| 77 | + data = struct.pack("d", time.time()) + data |
| 78 | + |
| 79 | + # Calculate the checksum on the data and the dummy header. |
| 80 | + my_checksum = checksum(header + data) |
| 81 | + |
| 82 | + # Now that we have the right checksum, we put that in. It's just easier |
| 83 | + # to make up a new header than to stuff it into the dummy. |
| 84 | + header = struct.pack( |
| 85 | + "bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1 |
| 86 | + ) |
| 87 | + packet = header + data |
| 88 | + my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1 |
| 89 | + |
| 90 | + |
| 91 | +def ping(dest_addr, timeout): |
| 92 | + """ |
| 93 | + Returns either the delay (in seconds) or none on timeout. |
| 94 | + """ |
| 95 | + icmp = socket.getprotobyname("icmp") |
| 96 | + try: |
| 97 | + my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) |
| 98 | + except socket.error, (errno, msg): |
| 99 | + if errno == 1: |
| 100 | + # Operation not permitted |
| 101 | + msg = msg + ( |
| 102 | + " - Note that ICMP messages can only be sent from processes" |
| 103 | + " running as root." |
| 104 | + ) |
| 105 | + raise socket.error(msg) |
| 106 | + raise # raise the original error |
| 107 | + |
| 108 | + my_ID = os.getpid() & 0xFFFF |
| 109 | + |
| 110 | + send_one_ping(my_socket, dest_addr, my_ID) |
| 111 | + delay = receive_one_ping(my_socket, my_ID, timeout) |
| 112 | + |
| 113 | + my_socket.close() |
| 114 | + return delay |
0 commit comments