moved
[gscan_quic.git] / ping.go
blobd058130e8a27e74ae215ad75d14163cb73f9357b
1 package main
3 // Copyright 2009 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
7 // taken from http://golang.org/src/pkg/net/ipraw_test.go
9 import (
10 "bytes"
11 "errors"
12 "net"
13 "os"
14 "time"
17 func testPing(ip string, config *ScanConfig, record *ScanRecord) bool {
18 start := time.Now()
19 if err := Pinger(ip, config.ScanMaxRTT); err != nil {
20 return false
22 if rtt := time.Since(start); rtt > config.ScanMinRTT {
23 record.RTT += rtt
24 return true
26 return false
29 const (
30 icmpv4EchoRequest = 8
31 icmpv4EchoReply = 0
32 icmpv6EchoRequest = 128
33 icmpv6EchoReply = 129
36 var ErrPingConnFailed = errors.New("ping: connect failed")
38 type icmpMessage struct {
39 Type int // type
40 Code int // code
41 Checksum int // checksum
42 Body icmpMessageBody // body
45 type icmpMessageBody interface {
46 Len() int
47 Marshal() ([]byte, error)
50 // Marshal returns the binary enconding of the ICMP echo request or
51 // reply message m.
52 func (m *icmpMessage) Marshal() ([]byte, error) {
53 b := []byte{byte(m.Type), byte(m.Code), 0, 0}
54 if m.Body != nil && m.Body.Len() != 0 {
55 mb, err := m.Body.Marshal()
56 if err != nil {
57 return nil, err
59 b = append(b, mb...)
61 switch m.Type {
62 case icmpv6EchoRequest, icmpv6EchoReply:
63 return b, nil
65 csumcv := len(b) - 1 // checksum coverage
66 s := uint32(0)
67 for i := 0; i < csumcv; i += 2 {
68 s += uint32(b[i+1])<<8 | uint32(b[i])
70 if csumcv&1 == 0 {
71 s += uint32(b[csumcv])
73 s = s>>16 + s&0xffff
74 s = s + s>>16
75 // Place checksum back in header; using ^= avoids the
76 // assumption the checksum bytes are zero.
77 b[2] ^= byte(^s & 0xff)
78 b[3] ^= byte(^s >> 8)
79 return b, nil
82 // parseICMPMessage parses b as an ICMP message.
83 func parseICMPMessage(b []byte) (*icmpMessage, error) {
84 msglen := len(b)
85 if msglen < 4 {
86 return nil, errors.New("message too short")
88 m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
89 if msglen > 4 {
90 var err error
91 switch m.Type {
92 case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply:
93 m.Body, err = parseICMPEcho(b[4:])
94 if err != nil {
95 return nil, err
99 return m, nil
102 // imcpEcho represenets an ICMP echo request or reply message body.
103 type icmpEcho struct {
104 ID int // identifier
105 Seq int // sequence number
106 Data []byte // data
109 func (p *icmpEcho) Len() int {
110 if p == nil {
111 return 0
113 return 4 + len(p.Data)
116 // Marshal returns the binary enconding of the ICMP echo request or
117 // reply message body p.
118 func (p *icmpEcho) Marshal() ([]byte, error) {
119 b := make([]byte, 4+len(p.Data))
120 b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff)
121 b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff)
122 copy(b[4:], p.Data)
123 return b, nil
126 // parseICMPEcho parses b as an ICMP echo request or reply message body.
127 func parseICMPEcho(b []byte) (*icmpEcho, error) {
128 bodylen := len(b)
129 p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
130 if bodylen > 4 {
131 p.Data = make([]byte, bodylen-4)
132 copy(p.Data, b[4:])
134 return p, nil
137 func Ping(address string, timeout time.Duration) error {
138 return Pinger(address, timeout)
141 func Pinger(address string, timeout time.Duration) error {
142 typ := icmpv4EchoRequest
143 network := "ip4:icmp"
145 isIpv6 := false
146 if ip := net.ParseIP(address); ip != nil && ip.To4() == nil {
147 typ = icmpv6EchoRequest
148 network = "ip6:ipv6-icmp"
149 isIpv6 = true
152 c, err := net.Dial(network, address)
153 if err != nil {
154 return ErrPingConnFailed
156 defer c.Close()
157 deadline := time.Now().Add(timeout)
158 c.SetReadDeadline(deadline)
160 xid, xseq := os.Getpid()&0xffff, 1
161 wb, err := (&icmpMessage{
162 Type: typ, Code: 0,
163 Body: &icmpEcho{
164 ID: xid, Seq: xseq,
165 Data: bytes.Repeat([]byte("Go Go Gadget Ping!!!"), 3),
167 }).Marshal()
168 if err != nil {
169 return err
171 if _, err = c.Write(wb); err != nil {
172 return err
174 var m *icmpMessage
175 rb := make([]byte, 20+len(wb))
176 for {
177 // read_timeout := deadline.Sub(time.Now())
178 // c.SetReadDeadline(time.Now().Add(read_timeout))
179 if _, err = c.Read(rb); err != nil {
180 return err
182 if !isIpv6 {
183 rb = ipv4Payload(rb)
185 if m, err = parseICMPMessage(rb); err != nil {
186 return err
188 switch m.Type {
189 case icmpv4EchoRequest, icmpv6EchoRequest:
190 continue
192 break
194 return nil
197 func ipv4Payload(b []byte) []byte {
198 if len(b) < 20 {
199 return b
201 hdrlen := int(b[0]&0x0f) << 2
202 return b[hdrlen:]