func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) { const host = "www.google.com" ips, err := net.LookupIP(host) if err != nil { return nil, err } netaddr := func(ip net.IP) (net.Addr, error) { switch c.LocalAddr().(type) { case *net.UDPAddr: return &net.UDPAddr{IP: ip}, nil case *net.IPAddr: return &net.IPAddr{IP: ip}, nil default: return nil, errors.New("neither UDPAddr nor IPAddr") } } for _, ip := range ips { switch protocol { case iana.ProtocolICMP: if ip.To4() != nil { return netaddr(ip) } case iana.ProtocolIPv6ICMP: if ip.To16() != nil && ip.To4() == nil { return netaddr(ip) } } } return nil, errors.New("no A or AAAA record") }
func (p *Pinger) recvICMP(conn *icmp.PacketConn, recv chan<- *packet, ctx *context, wg *sync.WaitGroup) { p.debugln("recvICMP(): Start") for { select { case <-ctx.stop: p.debugln("recvICMP(): <-ctx.stop") wg.Done() p.debugln("recvICMP(): wg.Done()") return default: } bytes := make([]byte, 512) conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) p.debugln("recvICMP(): ReadFrom Start") _, ra, err := conn.ReadFrom(bytes) p.debugln("recvICMP(): ReadFrom End") if err != nil { if neterr, ok := err.(*net.OpError); ok { if neterr.Timeout() { p.debugln("recvICMP(): Read Timeout") continue } else { p.debugln("recvICMP(): OpError happen", err) p.mu.Lock() ctx.err = err p.mu.Unlock() p.debugln("recvICMP(): close(ctx.done)") close(ctx.done) p.debugln("recvICMP(): wg.Done()") wg.Done() return } } } p.debugln("recvICMP(): p.recv <- packet") select { case recv <- &packet{bytes: bytes, addr: ra}: case <-ctx.stop: p.debugln("recvICMP(): <-ctx.stop") wg.Done() p.debugln("recvICMP(): wg.Done()") return } } }
func (p *Pinger) sendICMP(conn, conn6 *icmp.PacketConn) (map[string]*net.IPAddr, error) { p.debugln("sendICMP(): Start") p.mu.Lock() p.id = rand.Intn(0xffff) p.seq = rand.Intn(0xffff) p.mu.Unlock() queue := make(map[string]*net.IPAddr) wg := new(sync.WaitGroup) for key, addr := range p.addrs { var typ icmp.Type var cn *icmp.PacketConn if isIPv4(addr.IP) { typ = ipv4.ICMPTypeEcho cn = conn } else if isIPv6(addr.IP) { typ = ipv6.ICMPTypeEchoRequest cn = conn6 } else { continue } if cn == nil { continue } t := timeToBytes(time.Now()) if p.Size-TimeSliceLength != 0 { t = append(t, byteSliceOfSize(p.Size-TimeSliceLength)...) } p.mu.Lock() bytes, err := (&icmp.Message{ Type: typ, Code: 0, Body: &icmp.Echo{ ID: p.id, Seq: p.seq, Data: t, }, }).Marshal(nil) p.mu.Unlock() if err != nil { wg.Wait() return queue, err } queue[key] = addr var dst net.Addr = addr if p.network == "udp" { dst = &net.UDPAddr{IP: addr.IP, Zone: addr.Zone} } p.debugln("sendICMP(): Invoke goroutine") wg.Add(1) go func(conn *icmp.PacketConn, ra net.Addr, b []byte) { for { if _, err := conn.WriteTo(bytes, ra); err != nil { if neterr, ok := err.(*net.OpError); ok { if neterr.Err == syscall.ENOBUFS { continue } } } break } p.debugln("sendICMP(): WriteTo End") wg.Done() }(cn, dst, bytes) } wg.Wait() p.debugln("sendICMP(): End") return queue, nil }
func (p *Pinger) run(once bool) { p.debugln("Run(): Start") var conn, conn6 *icmp.PacketConn if p.hasIPv4 { if conn = p.listen(ipv4Proto[p.network], p.source); conn == nil { return } defer conn.Close() } if p.hasIPv6 { if conn6 = p.listen(ipv6Proto[p.network], p.source6); conn6 == nil { return } defer conn6.Close() } recv := make(chan *packet, 1) recvCtx := newContext() wg := new(sync.WaitGroup) p.debugln("Run(): call recvICMP()") if conn != nil { wg.Add(1) go p.recvICMP(conn, recv, recvCtx, wg) } if conn6 != nil { wg.Add(1) go p.recvICMP(conn6, recv, recvCtx, wg) } p.debugln("Run(): call sendICMP()") queue, err := p.sendICMP(conn, conn6) ticker := time.NewTicker(p.MaxRTT) mainloop: for { select { case <-p.ctx.stop: p.debugln("Run(): <-p.ctx.stop") break mainloop case <-recvCtx.done: p.debugln("Run(): <-recvCtx.done") p.mu.Lock() err = recvCtx.err p.mu.Unlock() break mainloop case <-ticker.C: p.mu.Lock() handler := p.OnIdle p.mu.Unlock() if handler != nil { handler() } if once || err != nil { break mainloop } p.debugln("Run(): call sendICMP()") queue, err = p.sendICMP(conn, conn6) case r := <-recv: p.debugln("Run(): <-recv") p.procRecv(r, queue) } } ticker.Stop() p.debugln("Run(): close(recvCtx.stop)") close(recvCtx.stop) p.debugln("Run(): wait recvICMP()") wg.Wait() p.mu.Lock() p.ctx.err = err p.mu.Unlock() p.debugln("Run(): close(p.ctx.done)") close(p.ctx.done) p.debugln("Run(): End") }