#!/usr/bin/python

# Copyright (C) 2006 Yannick Gingras <ygingras@ygingras.net>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

"""Simple implementation of ping.  This is mostly a translation of the
program found in Unix Network Programming by Richard Stevens."""

import socket as s
import time
import os
import sys

timeFunct = time.time

# from RFC 792 :
#   Echo or Echo Reply Message
#
#    0                   1                   2                   3
#    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#   |     Type      |     Code      |          Checksum             |
#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#   |           Identifier          |        Sequence Number        |
#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#   |     Data ...
#   +-+-+-+-+-

def printPacket(packet):
    for byte in packet:
        print "%02X" % ord(byte),
    print ""
    
def getChkSum(packet):
    # algo from RFC 1071
    sum = 0
    sumedPacket = packet
    if len(sumedPacket) % 2:
        sumedPacket += chr(0)

    while len(sumedPacket):
        high = sumedPacket[1]
        low = sumedPacket[0]
        sum += ord(high)*256 + ord(low)
        sumedPacket = sumedPacket[2:]

    # Fold 32-bit sum to 16 bits 
    sum = (sum & 0xffff) + (sum >> 16)
    sum += (sum >> 16)
    
    return ~sum;
   

def kickIpHead(packet):
    hlen = ord(packet[0])%16 # bit 4 to 7
    hlen *= 4
    return packet[hlen:]


def createPacket(seq):
    packet = ""
    packet += chr(8)
    packet += chr(0) # echo request type 0
    packet += chr( s.htons(0) / 256 ) # 0 checksum MSB
    packet += chr( s.htons(0) % 256 ) # 0 checksum LSB
    packet += chr( s.htons(os.getpid()) / 256 ) # pid MSB
    packet += chr( s.htons(os.getpid()) % 256 ) # pid LSB
    packet += chr( s.htons(seq) / 256 ) # seq MSB
    packet += chr( s.htons(seq) % 256 ) # seq LSB

    curTime = timeFunct()
    packet += "%s" % curTime

    # add checksum
    sum = getChkSum(packet)
    sumStr = chr( s.htons(sum) / 256 )
    sumStr += chr( s.htons(sum) % 256 )

    packet = packet[:2] + sumStr + packet[4:]

    return packet

def pingHost(sock, host, seq):
    packet = createPacket(seq)
    sock.sendto(packet, (host, 0))
    reply = sock.recvfrom(1024)[0]
    
    # save the reception time stamp
    recvTime = timeFunct()
    reply = kickIpHead(reply)

    # check ICMP ID and Sequence
    id = s.ntohs(ord(reply[4])*256 + ord(reply[5]))
    if id != os.getpid():
        raise Exception("Received Wrong Reply ID")

    replySeq = s.htons(ord(reply[6])*256 + ord(reply[7]))
    if replySeq != seq:
        raise Exception("Received Wrong Reply Sequence")

    sendTime = float(reply[8:])
    delay = recvTime - sendTime
    print "from %s: icmp_seq=%d time=%0.1f ms" % (host, seq, delay*1000)


if len(sys.argv) != 2:
    print """usage : pyping.py <host>
    where host is either and ip addr or a hostname"""
    sys.exit(1)
    
host = s.gethostbyname(sys.argv[1])

keepGoing = 1
sock = s.socket( s.AF_INET, s.SOCK_RAW, s.getprotobyname("icmp") )
seq = 1

while (keepGoing):
    pingHost(sock, host, seq)
    seq += 1
    time.sleep(0.5)
    
# arch-tag: 500ccf2c-02a4-4003-bc64-135050751569
