小默
【转】ping.py
差点勾成Jiong的分类了,因为最近真的很囧
#
!/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 <lars strand at gnist org>;
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 <lars at unik no>;
'
__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
<
int(struct.calcsize(
"
d
"
)):
_error(
"
packetsize to small, must be at least %d
"
%
int(struct.calcsize(
"
d
"
)))
#
construct header
if
ipv6:
header
=
struct.pack(
'
BbHHh
'
, ICMP_TYPE_IP6, ICMP_CODE, ICMP_CHECKSUM, \
ICMP_ID, ICMP_SEQ_NR
+
id)
else
:
header
=
struct.pack(
'
bbHHh
'
, ICMP_TYPE, ICMP_CODE, ICMP_CHECKSUM, \
ICMP_ID, ICMP_SEQ_NR
+
id)
#
if size big enough, embed this payload
load
=
"
-- IF YOU ARE READING THIS YOU ARE A NERD! --
"
#
space for time
size
-=
struct.calcsize(
"
d
"
)
#
construct payload based on size, may be omitted :)
rest
=
""
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.
"""
print
"
packet =
"
, packet
#
add byte if not dividable by 2
if
len(packet)
&
1
:
packet
=
packet
+
'
\0
'
print
"
packet = [after add byte]
"
, packet
#
split into 16-bit word and insert into a binary array
words
=
array.array(
'
h
'
, packet)
#
there's a big/little-endian issue
#
when analysis with wireshark
sum
=
0
print
"
words in packet array:
"
, words
#
perform ones complement arithmetic on 16-bit words
for
word
in
words:
print
"
process word:
"
, hex(word)
sum
+=
(word
&
0xffff
)
print
"
sum:
"
, hex(sum)
hi
=
sum
>>
16
print
"
hi:
"
, hex(hi)
lo
=
sum
&
0xffff
print
"
lo:
"
, hex(lo)
sum
=
hi
+
lo
#
one's complement
print
"
sum(=hi+lo):
"
, hex(sum)
sum
=
sum
+
(sum
>>
16
)
#
TODO: another one's complement, why we need this?
#
sum is at most 16 bit, so here do nothing.???
print
"
sum(=sum+(sum>>16)):
"
, hex(sum)
print
(
~
sum)
&
0xffff
#
TODO: the implement of one number is just ~ ?
return
(
~
sum)
&
0xffff
#
return ones complement
#
We know whether the host we will ping is alive before we call this function?
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?
#
TODO: if a ip is end with ".0", so it must be a network?
#
and why we cannot ping a network?
#
ICMP cannot broadcast?
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:
#
TODO: what does alive mean?
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
<=
number:
lost
+=
1
#
in case user hit ctrl-c
#
TODO: what does this mean?
#
create the IPv6/IPv4 socket
if
ipv6:
#
can not create a raw socket if not root or setuid to root
try
:
pingSocket
=
socket.socket(socket.AF_INET6, socket.SOCK_RAW, \
socket.getprotobyname(
"
ipv6-icmp
"
))
except
socket.error, e:
print
"
socket error: %s
"
%
e
_error(
"
You must be root (uses raw sockets)
"
%
os.path.basename(sys.argv[0]))
#
IPv4
else
:
#
can not create a raw socket if not root or setuid to root
try
:
pingSocket
=
socket.socket(socket.AF_INET, socket.SOCK_RAW, \
socket.getprotobyname(
"
icmp
"
))
except
socket.error, e:
print
"
socket error: %s
"
%
e
_error(
"
You must be root (%s uses raw sockets)
"
%
os.path.basename(sys.argv[0]))
packet
=
_construct(start, size, ipv6)
#
make a ping packet
#
send the ping
try
:
pingSocket.sendto(packet,(node,
1
))
except
socket.error, e:
_error(
"
socket error: %s
"
%
e)
#
reset values
pong
=
""
; iwtd
=
[]
#
wait until there is data in the socket
while
1
:
#
input, output, exceptional conditions
iwtd, owtd, ewtd
=
select.select([pingSocket], [], [], timeout)
break
#
no data and timout occurred
#
data on socket - this means we have an answer
if
iwtd:
#
ok, data on socket
endtime
=
time.time()
#
time packet received
#
read data (we only need the header)
pong, address
=
pingSocket.recvfrom(size
+
48
)
lost
-=
1
#
in case user hit ctrl-c
#
examine packet
#
fetch TTL from IP header
if
ipv6:
#
since IPv6 header and any extension header are never passed
#
to a raw socket, we can *not* get hoplimit field..
#
I hoped that a socket option would help, but it's not
#
supported:
#
pingSocket.setsockopt(IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1)
#
so we can't fetch hoplimit..
#
fetch hoplimit
#
rawPongHop = struct.unpack("c", pong[7])[0]
#
fetch pong header
pongHeader
=
pong[0:
8
]
pongType, pongCode, pongChksum, pongID, pongSeqnr
=
\
struct.unpack(
"
bbHHh
"
, pongHeader)
#
fetch starttime from pong
starttime
=
struct.unpack(
"
d
"
, pong[
8
:
16
])[0]
#
IPv4
else
:
#
time to live
rawPongHop
=
struct.unpack(
"
s
"
, pong[
8
])[0]
#
convert TTL from 8 bit to 16 bit integer
pongHop
=
int(binascii.hexlify(str(rawPongHop)),
16
)
#
fetch pong header
pongHeader
=
pong[
20
:
28
]
pongType, pongCode, pongChksum, pongID, pongSeqnr
=
\
struct.unpack(
"
bbHHh
"
, pongHeader)
#
fetch starttime from pong
starttime
=
struct.unpack(
"
d
"
, pong[
28
:
36
])[0]
#
valid ping packet received?
if
not
pongSeqnr
==
start:
pong
=
None
#
NO data on socket - timeout waiting for answer
if
not
pong:
if
alive:
print
"
no reply from %s (%s)
"
%
(str(node), str(host))
else
:
print
"
ping timeout: %s (icmp_seq=%d)
"
%
(host, start)
#
do not wait if just sending one packet
if
number
!=
1
and
start
<
number:
time.sleep(flood
^
1
)
start
+=
1
continue
#
lost a packet - try again
triptime
=
endtime
-
starttime
#
compute RRT
tsum
+=
triptime
#
triptime for all packets (stddev)
tsumsq
+=
triptime
*
triptime
#
triptime^2 for all packets (stddev)
#
compute statistic
maxt
=
max ((triptime, maxt))
mint
=
min ((triptime, mint))
if
alive:
print
str(node)
+
"
(
"
+
str(host)
+
"
) is alive
"
else
:
if
ipv6:
#
size + 8 = payload + header
print
"
%d bytes from %s: icmp_seq=%d time=%.5f ms
"
%
\
(size
+
8
, host, pongSeqnr, triptime
*
1000
)
else
:
print
"
%d bytes from %s: icmp_seq=%d ttl=%s time=%.5f ms
"
%
\
(size
+
8
, host, pongSeqnr, pongHop, triptime
*
1000
)
#
do not wait if just sending one packet
if
number
!=
1
and
start
<
number:
#
if flood = 1; do not sleep - just ping
time.sleep(flood
^
1
)
#
wait before send new packet
#
the last thing to do is update the counter - else the value
#
(can) get wrong when computing summary at the end (if user
#
hit ctrl-c when pinging)
start
+=
1
#
end ping send/recv while
#
if user ctrl-d or ctrl-c
except
(EOFError, KeyboardInterrupt):
#
if user disrupts ping, it is most likly done before
#
the counter get updates - if do not update it here, the
#
summary get all wrong.
start
+=
1
pass
#
compute and print som stats
#
stddev computation based on ping.c from FreeBSD
if
start
!=
0
or
lost
>
0:
#
do not print stats if 0 packet sent
start
-=
1
#
since while is '<='
avg
=
tsum
/
start
#
avg round trip
vari
=
tsumsq
/
start
-
avg
*
avg
#
%-packet lost
if
start
==
lost:
plost
=
100
else
:
plost
=
(lost
/
start)
*
100
if
not
alive:
print
"
\n--- %s ping statistics ---
"
%
node
print
"
%d packets transmitted, %d packets received, %d%% packet loss
"
%
\
(start, start
-
lost, plost)
#
don't display summary if 100% packet-loss
if
plost
!=
100
:
print
"
round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms
"
%
\
(mint
*
1000
, (tsum
/
start)
*
1000
, maxt
*
1000
, math.sqrt(vari)
*
1000
)
pingSocket.close()
def
_error(err):
"""
Exit if running standalone, else raise an exception
"""
if
__name__
==
'
__main__
'
:
print
"
%s: %s
"
%
(os.path.basename(sys.argv[0]), str(err))
print
"
Try `%s --help' for more information.
"
%
os.path.basename(sys.argv[0])
sys.exit(
1
)
else
:
raise
Exception, str(err)
def
_usage():
"""
Print usage if run as a standalone program
"""
print
"""
usage: %s [OPTIONS] HOST
Send ICMP ECHO_REQUEST packets to network hosts.
Mandatory arguments to long options are mandatory for short options too.
-c, --count=N Stop after sending (and receiving) 'N' ECHO_RESPONSE
packets.
-s, --size=S Specify the number of data bytes to be sent. The default
is 56, which translates into 64 ICMP data bytes when
combined with the 8 bytes of ICMP header data.
-f, --flood Flood ping. Outputs packets as fast as they come back. Use
with caution!
-6, --ipv6 Ping using IPv6.
-t, --timeout=s Specify a timeout, in seconds, before a ping packet is
considered 'lost'.
-h, --help Display this help and exit
Report bugs to lars [at] gnist org
"""
%
os.path.basename(sys.argv[0])
if
__name__
==
'
__main__
'
:
"""
Main loop
"""
#
version control
version
=
string.split(string.split(sys.version)[0][:
3
],
"
.
"
)
if
map(int, version)
<
[
2
,
3
]:
_error(
"
You need Python ver 2.3 or higher to run!
"
)
try
:
#
opts = arguments recognized,
#
args = arguments NOT recognized (leftovers)
opts, args
=
getopt.getopt(sys.argv[
1
:
-
1
],
"
hat:6c:fs:
"
, \
[
"
help
"
,
"
alive
"
,
"
timeout=
"
,
"
ipv6
"
, \
"
count=
"
,
"
flood
"
,
"
packetsize=
"
])
except
getopt.GetoptError:
#
print help information and exit:
_error(
"
illegal option(s) --
"
+
str(sys.argv[
1
:]))
#
test whether any host given
if
len(sys.argv)
>=
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
posted on 2010-10-25 22:16
小默
阅读(584)
评论(0)
编辑
收藏
引用
所属分类:
Network
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
相关文章:
zz 反向代理
httpget
CCNA
DNS解析的疑问
【转】DiG HOWTO
【转】网路协议
http 1.1 GET
python 给出ip,子网掩码,求网络内主机Ip列表
net helpmsg
【转】IP, Internet Protocol
网站导航:
博客园
IT新闻
BlogJava
知识库
博问
管理
导航
首页
新随笔
联系
聚合
管理
统计
随笔 - 289
文章 - 0
评论 - 84
引用 - 0
留言簿
(13)
给我留言
查看公开留言
查看私人留言
随笔分类
(287)
Algorithm(5)
(rss)
Database(7)
(rss)
DataStructure(1)
(rss)
Jiong(24)
(rss)
Language(68)
(rss)
Linux(54)
(rss)
Network(27)
(rss)
Security(23)
(rss)
Tools(14)
(rss)
Web(5)
(rss)
Windows(59)
(rss)
随笔档案
(289)
2013年8月 (1)
2012年2月 (1)
2011年10月 (2)
2011年9月 (10)
2011年6月 (6)
2011年3月 (5)
2011年2月 (1)
2011年1月 (4)
2010年12月 (2)
2010年11月 (10)
2010年10月 (6)
2010年9月 (3)
2010年8月 (23)
2010年7月 (7)
2010年6月 (16)
2010年5月 (16)
2010年4月 (37)
2010年3月 (15)
2010年2月 (38)
2010年1月 (5)
2009年12月 (50)
2009年11月 (5)
2009年10月 (23)
2009年9月 (3)
漏洞
sebug
搜索
积分与排名
积分 - 287161
排名 - 90
最新评论
1. re: django debug toolbar
其实,如果你访问不了谷歌,是用不了的,你还有修改一个地方
--berlin
2. re: 完美的 NSIS 安装脚本zz
路过留名。
--anopos
3. re: MFC - DECLARE_MESSAGE_MAP()
这个具体的有人回复吗?学习
--Stadium Series Hockey Jersey
4. re: MFC - #error include ’stdafx.h’ before including this file for PCH
学习了。O(∩_∩)O谢谢。。
--Stadium Series Hockey Jersey
5. re: Windows APC机制zzz
这么好的文章,才看到,惭愧
--zssure
阅读排行榜
1. MFC BEGIN_MESSAGE_MAP()(24558)
2. 【转】HTTP方法(7704)
3. 完美的 NSIS 安装脚本zz(7686)
4. ls命令是怎样实现的,getdents64,linux-2.6.27.5(7449)
5. TypeError: 'tuple' object does not support item assignment(5672)