- 论坛徽章:
- 0
|
从研读ping.py开始学习python
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
"""ping.py
ping.py uses the ICMP protocol's mandatory ECHO_REQUEST
datagram to elicit an ICMP ECHO_RESPONSE from a
host or gateway.
Copyright (C) 2004 - Lars Strand
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Must be running as root, or write a suid-wrapper. Since newer *nix
variants, the kernel ignores the set[ug]id flags on #! scripts for
security reasons
RFC792, 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 ...
+-+-+-+-+-
TODO:
- do not create socket inside 'while' (but if not: ipv6 won't work)
- add support for broadcast/multicast
- add support for own payload string
CHANGELOG:
DONE --> bugfix from Filip Van Raemdonck mechanix debian org
DONE --> add more support for modules (raise instead of sys.exit)
DONE --> locale func names
DONE --> package def
DONE --> some code cleanup
"""
import sys
import os
import struct
import array
import time
import select
import binascii
import math
import getopt
import string
import socket
# total size of data (payload)
ICMP_DATA_STR = 56
# initial values of header variables
ICMP_TYPE = 8
ICMP_TYPE_IP6 = 128
ICMP_CODE = 0
ICMP_CHECKSUM = 0
ICMP_ID = 0
ICMP_SEQ_NR = 0
# Package definitions.
__program__ = 'ping'
__version__ = '0.5a'
__date__ = '2004/15/12'
__author__ = 'Lars Strand '
__licence__ = 'GPL'
__copyright__ = 'Copyright (C) 2004 Lars Strand'
def _construct(id, size, ipv6):
"""Constructs a ICMP echo packet of variable size
"""
# size must be big enough to contain time sent
if size len(load):
rest = load
size -= len(load)
# pad the rest of payload
rest += size * "X"
# pack
data = struct.pack("d", time.time()) + rest
packet = header + data # ping packet without checksum
checksum = _in_cksum(packet) # make checksum
# construct header with correct checksum
if ipv6:
header = struct.pack('BbHHh', ICMP_TYPE_IP6, ICMP_CODE, checksum,
ICMP_ID, ICMP_SEQ_NR+id)
else:
header = struct.pack('bbHHh', ICMP_TYPE, ICMP_CODE, checksum, ICMP_ID,
ICMP_SEQ_NR+id)
# ping packet *with* checksum
packet = header + data
# a perfectly formatted ICMP echo packet
return packet
def _in_cksum(packet):
"""THE RFC792 states: 'The 16 bit one's complement of
the one's complement sum of all 16 bit words in the header.'
Generates a checksum of a (ICMP) packet. Based on in_chksum found
in ping.c on FreeBSD.
"""
# add byte if not dividable by 2
if len(packet) & 1:
packet = packet + ' '
# split into 16-bit word and insert into a binary array
words = array.array('h', packet)
sum = 0
# perform ones complement arithmetic on 16-bit words
for word in words:
sum += (word & 0xffff)
hi = sum >> 16
lo = sum & 0xffff
sum = hi + lo
sum = sum + (sum >> 16)
return (~sum) & 0xffff # return ones complement
def pingNode(alive=0, timeout=1.0, ipv6=0, number=sys.maxint, node=None,
flood=0, size=ICMP_DATA_STR):
"""Pings a node based on input given to the function.
"""
# if no node, exit
if not node:
_error("")
# if not a valid host, exit
if ipv6:
if socket.has_ipv6:
try:
info, port = socket.getaddrinfo(node, None)
host = info[4][0]
# do not print ipv6 twice if ipv6 address given as node
if host == node:
noPrintIPv6adr = 1
except:
_error("cannot resolve %s: Unknow host" % node)
else:
_error("No support for IPv6 on this plattform")
else: # IPv4
try:
host = socket.gethostbyname(node)
except:
_error("cannot resolve %s: Unknow host" % node)
# trying to ping a network?
if not ipv6:
if int(string.split(host, ".")[-1]) == 0:
_error("no support for network ping")
# do some sanity check
if number == 0:
_error("invalid count of packets to transmit: '%s'" % str(a))
if alive:
number = 1
# Send the ping(s)
start = 1; mint = 999; maxt = 0.0; avg = 0.0
lost = 0; tsum = 0.0; tsumsq = 0.0
# tell the user what we do
if not alive:
if ipv6:
# do not print the ipv6 twice if ip adress given as node
# (it can be to long in term window)
if noPrintIPv6adr == 1:
# add 40 (header) + 8 (icmp header) + payload
print "PING %s : %d data bytes (40+8+%d)" % (str(node),
40+8+size, size)
else:
# add 40 (header) + 8 (icmp header) + payload
print "PING %s (%s): %d data bytes (40+8+%d)" % (str(node),
str(host), 40+8+size, size)
else:
# add 20 (header) + 8 (icmp header) + payload
print "PING %s (%s): %d data bytes (20+8+%d)" % (str(node), str(host),
20+8+size, size)
# trap ctrl-d and ctrl-c
try:
# send the number of ping packets as given
while start 0: # do not print stats if 0 packet sent
start -= 1 # since while is '= 2:
node = sys.argv[-1:][0] # host to be pinged
if node[0] == '-' or node == '-h' or node == '--help' :
_usage()
else:
_error("No arguments given")
if args:
_error("illegal option -- %s" % str(args))
# default variables
alive = 0; timeout = 1.0; ipv6 = 0; count = sys.maxint;
flood = 0; size = ICMP_DATA_STR
# run through arguments and set variables
for o, a in opts:
if o == "-h" or o == "--help": # display help and exit
_usage()
sys.exit(0)
if o == "-t" or o == "--timeout": # timeout before "lost"
try:
timeout = float(a)
except:
_error("invalid timout: '%s'" % str(a))
if o == "-6" or o == "--ipv6": # ping ipv6
ipv6 = 1
if o == "-c" or o == "--count": # how many pings?
try:
count = int(a)
except:
_error("invalid count of packets to transmit: '%s'" % str(a))
if o == "-f" or o == "--flood": # no delay between ping send
flood = 1
if o == "-s" or o == "--packetsize": # set the ping payload size
try:
size = int(a)
except:
_error("invalid packet size: '%s'" % str(a))
# just send one packet and say "it's alive"
if o == "-a" or o == "--alive":
alive = 1
# here we send
pingNode(alive=alive, timeout=timeout, ipv6=ipv6, number=count,
node=node, flood=flood, size=size)
# if we made it this far, do a clean exit
sys.exit(0)
### end
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/9868/showart_46950.html |
|