#!/usr/bin/env python2.7
# vim: set fileencoding: utf-8
'''
Test network latency by creating TCP sockets and timing the 3-way handshake

compat-check: 2.6 2.7

notes: expert mode is a work in progress, but not using it shouldn't affect
normal user mode.

NOTES:
    * Modified to ensure a DNS lookup is done every time
'''

__AUTHOR__ = 'Peter Downs <padowns@gmail.com>'
__VERSION__ = '0.2.3'
__DATE__ = '2016-02-26'

import platform
__major_version, __minor_version = platform.python_version().split(".")[:2]

from datetime import datetime
import signal
import sys
import os
import socket
import select
import time
import timeit
import string
import getopt
import struct

total_conns = 0
times = []

def signal_handler(signal, frame):
    '''
    used to catch ctrl-c or SIGINT when the user wants to interrupt and terminate the program
    '''
    print_statistics()
    sys.exit(0)

def checksum(msg):
    s = 0
    for i in range(0, len(msg), 2):
        w = ord(msg[i]) + (ord(msg[i+1]) << 8 )
        s = s + w
    s = (s>>16) + (s & 0xffff);
    s = s + (s >> 16);
    s = ~s & 0xffff
    return s

def stddev(list):
    '''
    Let list = a list of numbers
    Given a list with len > 0, compute the standard deviation of the data set
    '''
    if (len(list) > 0):
        from math import sqrt
        mean = float(sum(list) / len(list))
        error = [ (list[i]-mean)**2 for i in range(len(list)) ]
        return sqrt(float(sum(error)/len(error)))
    else:
        return 0

def print_statistics():
    global total_conns
    global times
    print('\n--- tcpping statistics ---')
    if (len(times) == 0):
        print('No times were recorded.')
        return 0
    print('%d connections attempted, %d complete, %d failed, %.2f%% success rate, time %f ms') % (total_conns, len(times), total_conns-len(times), float(len(times))/float(total_conns)*100.0, sum(times))
    print('rtt min/avg/max/mdev = %f/%f/%f/%f ms') % (min(times), sum(times)/len(times), max(times), stddev(times))

signal.signal(signal.SIGINT, signal_handler)

def usage():
    print('tcpping version %s (c)%s %s') % (__VERSION__, __DATE__, __AUTHOR__)
    print('usage: tcpping [ -h ] [ -c <count> ] [ -r <rate> ] -d <host> -p <port> [ -m ] [ -s ] [ -t <timeout> ] [ -x ] [ -v ]')
    print('')
        print(' short options:')
        print('         -h - display this help message')
        print('         -m - output timestamps with microsecond precision - useful for comparing with tcpdump or strace output')
        print('         -s - output source ip and port per connection')
        print('         -x - expert mode; require root')
        print('         -v - print version and exit')
        print('')
    print('    long options:')
    print('            --help')
    print('            --dest | --destination <host>')
    print('            --port <port>')
    print('            --count <count>')
    print('         --rate <rate>')
        print('         --microseconds')
        print('         --timeout <timeout>')
        print('         --expert')
        print('         --version')
    print('')
    print('    host    - host or ip address to connect to')
    print('    port    - port to connect to on <host>')
    print('    count   - number of connections to try.  Default is infinite')
    print('    rate    - sleep time (in seconds) between connections.  Default is 1.')
    print('           Use a floating point number for microsecond precision (e.g. 0.200 for every 200ms)')
        print(' timeout - connection timeout in seconds')
        print(' microseconds - output timestamps with microsecond precision')
        print(' expert  - enable expert mode; requires root')
        print(' version - print version and exit')
    print('')

def main(argv):
        global total_conns
        global times

        __EXPERT__ = False

    # Defaults
        TIMEOUT = None
        SRC = None
        SRC_FLAG = 0
    DEST = None
    PORT = None
    COUNT = -1  # ping until interrupted
    RATE = 1    # ping every second
        MS_FLAG = 0

    try:
            opts, args = getopt.getopt(sys.argv[1:], "c:hd:mp:r:st:xv", ["count=", "help", "dest=", "destination=", "microseconds", "rate=", "port=", "source", "src", "timeout=", "expert", "version"])
    except getopt.GetoptError:
        usage()
        sys.exit(2)
    for o, a in opts:
        if o in ("-c", "--count"):
            COUNT = a
        if o in ("-r", "--rate"):
            RATE = a
        if o in ("-d", "--dest", "--destination"):
            DEST = a
                if o in ("-m", "--microseconds"):
                        MS_FLAG = 1
        if o in ("-p", "--port"):
            PORT = a
                if o in ("-s", "--source", "--src"):
                        SRC_FLAG = 1
                if o in ("-t", "--timeout"):
                        TIMEOUT = float(a)
                        socket.setdefaulttimeout(TIMEOUT)
        if o in ("-h", "--help"):
            usage()
            sys.exit(1)
                if o in ("-x", "--expert"):
                        __EXPERT__ = True
                if o in ("-v", "--version"):
                    print('tcpping version %s') % (__VERSION__)
                        sys.exit(0)

        if (__EXPERT__ and os.geteuid() != 0):
                print('Expert mode requires root privileges.')
                sys.exit(1)

    if (DEST==None or PORT==None):
        usage()
        sys.exit(2)

        if (float(RATE) < 0.0001):
                print('Connection rate should be greater than 0.0001 sec')
                sys.exit(3)

        if (__EXPERT__):
            #Initialize TCP and IP header fields
            packet=''

            ip_ihl = 5
            ip_ver = 4
            ip_tos = 0
            ip_tot_len = 0
            ip_id = 10101
            ip_frag_off = 0
            ip_ttl = 255
            ip_proto = socket.IPPROTO_TCP
            ip_check = 0
            ip_saddr = socket.inet_aton ( src_ip )
            ip_daddr = socket.inet_aton ( dst_ip )
            ip_ihl_ver = (ip_ver << 4) + ip_ihl
            ip_header = struct.pack('!BBHHHBBH4s4s' , ip_ihl_ver, ip_tos, ip_tot_len, ip_id, ip_frag_off, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr)

            tcp_source = 1234   # source port
            tcp_dest = PORT
            tcp_seq = 454
            tcp_ack_seq = 0
            tcp_doff = 5    #4 bit field, size of tcp header, 5 * 4 = 20 bytes
            #tcp flags
            tcp_fin = 0
            tcp_syn = 1
            tcp_rst = 0
            tcp_psh = 0
            tcp_ack = 0
            tcp_urg = 0
            tcp_window = socket.htons (5840)    #   maximum allowed window size
            tcp_check = 0
            tcp_urg_ptr = 0
            tcp_offset_res = (tcp_doff << 4) + 0
            tcp_flags = tcp_fin + (tcp_syn << 1) + (tcp_rst << 2) + (tcp_psh <<3) + (tcp_ack << 4) + (tcp_urg << 5)
            tcp_header = struct.pack('!HHLLBBHHH' , tcp_source, tcp_dest, tcp_seq, tcp_ack_seq, tcp_offset_res, tcp_flags,  tcp_window, tcp_check, tcp_urg_ptr)

    while int(total_conns) != int(COUNT):
            #port = socket.getservbyname(PORT)
            hostname = socket.gethostname()
            src_ip = socket.gethostbyname(hostname)
            dst_ip = socket.gethostbyname(DEST)

            total_conns += 1
            seq = total_conns
            src_port = "-1"
            dst_port = PORT
            if(not __EXPERT__):
                if MS_FLAG:
                    date = datetime.now().strftime("%H:%M:%S.%f")
                else:
                    date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

                try:
                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                except socket.error, msg:
                    print('Unable to obtain a socket (%s)') % (msg)
                    sys.exit(4)

                begin_time = time.time()
                try:
                        s.connect((DEST, int(PORT)))
                        if TIMEOUT != None:
                            fd=s.fileno()
                            rlist, wlist, xlist = select.select([],[fd],[],TIMEOUT)
                            if wlist == []:
                                raise socket.timeout
                except socket.timeout, msg:
            end_time = time.time()
                        connect_time = (end_time - begin_time) * 1000
                        RESULT = "%f ms Connection Failed (%s)" % (connect_time, msg)
                except socket.error, msg:
                        RESULT = '0ms Connection Failed (%s)' % (msg)
                else:
            end_time = time.time()

                        src_ip, src_port = s.getsockname()
                        dst_ip, dst_port = s.getpeername()

                        connect_time = (end_time - begin_time) * 1000
                        if (connect_time == 0.000000):
                            RESULT = "0.000000 ms Spurious measurement discarded"
                        else:
                            times.append(connect_time)
                            RESULT = "%f ms Connection Succeeded" % (connect_time)
                        s.shutdown(socket.SHUT_RDWR)
                        s.close()

                if SRC_FLAG:
                    output_fmt = "%(date)s %(src_ip)s:%(src_port)s %(DEST)s:%(PORT)s (%(dst_ip)s:%(dst_port)s) seq=%(seq)s %(RESULT)s"
                else:
                    output_fmt = "%(date)s %(DEST)s:%(PORT)s (%(dst_ip)s:%(dst_port)s) seq=%(seq)s %(RESULT)s"
                print(output_fmt) % vars()
                sys.stdout.flush()
                time.sleep(float(RATE))

            else:
                # Open a RAW socket so IP headers can be manipulated directly
                try:
                    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
                except socket.error, msg:
                    print('Unable to obtain a socket.')
                    print('Errno: ' + str(msg[0]) + ' Message: ' + msg[1])
                    sys.exit(4)

                packet = ''

        print_statistics()

if __name__ == '__main__':
        sys.exit(main(sys.argv))

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Captcha Code