-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathping.go
129 lines (102 loc) · 2.17 KB
/
ping.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package z
import (
"bytes"
"net"
"os"
"time"
)
const (
ICMP_ECHO_REQUEST = 8
ICMP_ECHO_REPLY = 0
)
// Ping Request
func makePingRequest(id, seq, pktlen int, filler []byte) []byte {
p := make([]byte, pktlen)
copy(p[8:], bytes.Repeat(filler, (pktlen-8)/len(filler)+1))
p[0] = ICMP_ECHO_REQUEST // type
p[1] = 0 // code
p[2] = 0 // cksum
p[3] = 0 // cksum
p[4] = uint8(id >> 8) // id
p[5] = uint8(id & 0xff) // id
p[6] = uint8(seq >> 8) // sequence
p[7] = uint8(seq & 0xff) // sequence
// calculate icmp checksum
cklen := len(p)
s := uint32(0)
for i := 0; i < (cklen - 1); i += 2 {
s += uint32(p[i+1])<<8 | uint32(p[i])
}
if cklen&1 == 1 {
s += uint32(p[cklen-1])
}
s = (s >> 16) + (s & 0xffff)
s = s + (s >> 16)
// place checksum back in header; using ^= avoids the
// assumption the checksum bytes are zero
p[2] ^= uint8(^s & 0xff)
p[3] ^= uint8(^s >> 8)
return p
}
func parsePingReply(p []byte) (id, seq int) {
id = int(p[4])<<8 | int(p[5])
seq = int(p[6])<<8 | int(p[7])
return
}
// Ping
func Ping(addr string, i int) bool {
// *IPAddr
raddr, e := net.ResolveIPAddr("ip4", addr)
if e != nil {
return false
}
// *IPConn
ipconn, ee := net.DialIP("ip4:icmp", nil, raddr)
if ee != nil {
return false
}
// 保证连接正常关闭
defer ipconn.Close()
// PID
sendid := os.Getpid() & 0xffff
sendseq := 1
pingpktlen := 64
for {
sendpkt := makePingRequest(sendid, sendseq, pingpktlen, []byte("Go Ping"))
// 发送请求
n, err := ipconn.WriteToIP(sendpkt, raddr)
if err != nil || n != pingpktlen {
break
}
// 超时
ipconn.SetDeadline(time.Now().Add(5 * time.Second))
// 返回数据
resp := make([]byte, 1024)
for {
// 读取返回
_, _, err := ipconn.ReadFrom(resp)
if err != nil {
break
}
// 判断状态
if resp[0] != ICMP_ECHO_REPLY {
continue
}
// 判断状态
rcvid, rcvseq := parsePingReply(resp)
if rcvid != sendid || rcvseq != sendseq {
break
}
// 成功返回
return true
}
// 执行次数内未成功返回
if i == sendseq {
break
}
// 计数器
sendseq++
}
// 失败返回
return false
}