github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/net/icmp/listen_posix.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows
     6  // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
     7  
     8  package icmp
     9  
    10  import (
    11  	"net"
    12  	"os"
    13  	"runtime"
    14  	"syscall"
    15  
    16  	"github.com/hxx258456/ccgo/net/internal/iana"
    17  	"github.com/hxx258456/ccgo/net/ipv4"
    18  	"github.com/hxx258456/ccgo/net/ipv6"
    19  )
    20  
    21  const sysIP_STRIPHDR = 0x17 // for now only darwin supports this option
    22  
    23  // ListenPacket listens for incoming ICMP packets addressed to
    24  // address. See net.Dial for the syntax of address.
    25  //
    26  // For non-privileged datagram-oriented ICMP endpoints, network must
    27  // be "udp4" or "udp6". The endpoint allows to read, write a few
    28  // limited ICMP messages such as echo request and echo reply.
    29  // Currently only Darwin and Linux support this.
    30  //
    31  // Examples:
    32  //	ListenPacket("udp4", "192.168.0.1")
    33  //	ListenPacket("udp4", "0.0.0.0")
    34  //	ListenPacket("udp6", "fe80::1%en0")
    35  //	ListenPacket("udp6", "::")
    36  //
    37  // For privileged raw ICMP endpoints, network must be "ip4" or "ip6"
    38  // followed by a colon and an ICMP protocol number or name.
    39  //
    40  // Examples:
    41  //	ListenPacket("ip4:icmp", "192.168.0.1")
    42  //	ListenPacket("ip4:1", "0.0.0.0")
    43  //	ListenPacket("ip6:ipv6-icmp", "fe80::1%en0")
    44  //	ListenPacket("ip6:58", "::")
    45  func ListenPacket(network, address string) (*PacketConn, error) {
    46  	var family, proto int
    47  	switch network {
    48  	case "udp4":
    49  		family, proto = syscall.AF_INET, iana.ProtocolICMP
    50  	case "udp6":
    51  		family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP
    52  	default:
    53  		i := last(network, ':')
    54  		if i < 0 {
    55  			i = len(network)
    56  		}
    57  		switch network[:i] {
    58  		case "ip4":
    59  			proto = iana.ProtocolICMP
    60  		case "ip6":
    61  			proto = iana.ProtocolIPv6ICMP
    62  		}
    63  	}
    64  	var cerr error
    65  	var c net.PacketConn
    66  	switch family {
    67  	case syscall.AF_INET, syscall.AF_INET6:
    68  		s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto)
    69  		if err != nil {
    70  			return nil, os.NewSyscallError("socket", err)
    71  		}
    72  		if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && family == syscall.AF_INET {
    73  			if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil {
    74  				syscall.Close(s)
    75  				return nil, os.NewSyscallError("setsockopt", err)
    76  			}
    77  		}
    78  		sa, err := sockaddr(family, address)
    79  		if err != nil {
    80  			syscall.Close(s)
    81  			return nil, err
    82  		}
    83  		if err := syscall.Bind(s, sa); err != nil {
    84  			syscall.Close(s)
    85  			return nil, os.NewSyscallError("bind", err)
    86  		}
    87  		f := os.NewFile(uintptr(s), "datagram-oriented icmp")
    88  		c, cerr = net.FilePacketConn(f)
    89  		f.Close()
    90  	default:
    91  		c, cerr = net.ListenPacket(network, address)
    92  	}
    93  	if cerr != nil {
    94  		return nil, cerr
    95  	}
    96  	switch proto {
    97  	case iana.ProtocolICMP:
    98  		return &PacketConn{c: c, p4: ipv4.NewPacketConn(c)}, nil
    99  	case iana.ProtocolIPv6ICMP:
   100  		return &PacketConn{c: c, p6: ipv6.NewPacketConn(c)}, nil
   101  	default:
   102  		return &PacketConn{c: c}, nil
   103  	}
   104  }