gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/net/internal/socket/sys_posix.go (about)

     1  // Copyright 2017 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 || zos
     6  // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
     7  
     8  package socket
     9  
    10  import (
    11  	"encoding/binary"
    12  	"errors"
    13  	"net"
    14  	"runtime"
    15  	"strconv"
    16  	"sync"
    17  	"time"
    18  )
    19  
    20  // marshalInetAddr writes a in sockaddr format into the buffer b.
    21  // The buffer must be sufficiently large (sizeofSockaddrInet4/6).
    22  // Returns the number of bytes written.
    23  func marshalInetAddr(a net.Addr, b []byte) int {
    24  	switch a := a.(type) {
    25  	case *net.TCPAddr:
    26  		return marshalSockaddr(a.IP, a.Port, a.Zone, b)
    27  	case *net.UDPAddr:
    28  		return marshalSockaddr(a.IP, a.Port, a.Zone, b)
    29  	case *net.IPAddr:
    30  		return marshalSockaddr(a.IP, 0, a.Zone, b)
    31  	default:
    32  		return 0
    33  	}
    34  }
    35  
    36  func marshalSockaddr(ip net.IP, port int, zone string, b []byte) int {
    37  	if ip4 := ip.To4(); ip4 != nil {
    38  		switch runtime.GOOS {
    39  		case "android", "illumos", "linux", "solaris", "windows":
    40  			NativeEndian.PutUint16(b[:2], uint16(sysAF_INET))
    41  		default:
    42  			b[0] = sizeofSockaddrInet4
    43  			b[1] = sysAF_INET
    44  		}
    45  		binary.BigEndian.PutUint16(b[2:4], uint16(port))
    46  		copy(b[4:8], ip4)
    47  		return sizeofSockaddrInet4
    48  	}
    49  	if ip6 := ip.To16(); ip6 != nil && ip.To4() == nil {
    50  		switch runtime.GOOS {
    51  		case "android", "illumos", "linux", "solaris", "windows":
    52  			NativeEndian.PutUint16(b[:2], uint16(sysAF_INET6))
    53  		default:
    54  			b[0] = sizeofSockaddrInet6
    55  			b[1] = sysAF_INET6
    56  		}
    57  		binary.BigEndian.PutUint16(b[2:4], uint16(port))
    58  		copy(b[8:24], ip6)
    59  		if zone != "" {
    60  			NativeEndian.PutUint32(b[24:28], uint32(zoneCache.index(zone)))
    61  		}
    62  		return sizeofSockaddrInet6
    63  	}
    64  	return 0
    65  }
    66  
    67  func parseInetAddr(b []byte, network string) (net.Addr, error) {
    68  	if len(b) < 2 {
    69  		return nil, errors.New("invalid address")
    70  	}
    71  	var af int
    72  	switch runtime.GOOS {
    73  	case "android", "illumos", "linux", "solaris", "windows":
    74  		af = int(NativeEndian.Uint16(b[:2]))
    75  	default:
    76  		af = int(b[1])
    77  	}
    78  	var ip net.IP
    79  	var zone string
    80  	if af == sysAF_INET {
    81  		if len(b) < sizeofSockaddrInet4 {
    82  			return nil, errors.New("short address")
    83  		}
    84  		ip = make(net.IP, net.IPv4len)
    85  		copy(ip, b[4:8])
    86  	}
    87  	if af == sysAF_INET6 {
    88  		if len(b) < sizeofSockaddrInet6 {
    89  			return nil, errors.New("short address")
    90  		}
    91  		ip = make(net.IP, net.IPv6len)
    92  		copy(ip, b[8:24])
    93  		if id := int(NativeEndian.Uint32(b[24:28])); id > 0 {
    94  			zone = zoneCache.name(id)
    95  		}
    96  	}
    97  	switch network {
    98  	case "tcp", "tcp4", "tcp6":
    99  		return &net.TCPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[2:4])), Zone: zone}, nil
   100  	case "udp", "udp4", "udp6":
   101  		return &net.UDPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[2:4])), Zone: zone}, nil
   102  	default:
   103  		return &net.IPAddr{IP: ip, Zone: zone}, nil
   104  	}
   105  }
   106  
   107  // An ipv6ZoneCache represents a cache holding partial network
   108  // interface information. It is used for reducing the cost of IPv6
   109  // addressing scope zone resolution.
   110  //
   111  // Multiple names sharing the index are managed by first-come
   112  // first-served basis for consistency.
   113  type ipv6ZoneCache struct {
   114  	sync.RWMutex                // guard the following
   115  	lastFetched  time.Time      // last time routing information was fetched
   116  	toIndex      map[string]int // interface name to its index
   117  	toName       map[int]string // interface index to its name
   118  }
   119  
   120  var zoneCache = ipv6ZoneCache{
   121  	toIndex: make(map[string]int),
   122  	toName:  make(map[int]string),
   123  }
   124  
   125  // update refreshes the network interface information if the cache was last
   126  // updated more than 1 minute ago, or if force is set. It returns whether the
   127  // cache was updated.
   128  func (zc *ipv6ZoneCache) update(ift []net.Interface, force bool) (updated bool) {
   129  	zc.Lock()
   130  	defer zc.Unlock()
   131  	now := time.Now()
   132  	if !force && zc.lastFetched.After(now.Add(-60*time.Second)) {
   133  		return false
   134  	}
   135  	zc.lastFetched = now
   136  	if len(ift) == 0 {
   137  		var err error
   138  		if ift, err = net.Interfaces(); err != nil {
   139  			return false
   140  		}
   141  	}
   142  	zc.toIndex = make(map[string]int, len(ift))
   143  	zc.toName = make(map[int]string, len(ift))
   144  	for _, ifi := range ift {
   145  		zc.toIndex[ifi.Name] = ifi.Index
   146  		if _, ok := zc.toName[ifi.Index]; !ok {
   147  			zc.toName[ifi.Index] = ifi.Name
   148  		}
   149  	}
   150  	return true
   151  }
   152  
   153  func (zc *ipv6ZoneCache) name(zone int) string {
   154  	updated := zoneCache.update(nil, false)
   155  	zoneCache.RLock()
   156  	name, ok := zoneCache.toName[zone]
   157  	zoneCache.RUnlock()
   158  	if !ok && !updated {
   159  		zoneCache.update(nil, true)
   160  		zoneCache.RLock()
   161  		name, ok = zoneCache.toName[zone]
   162  		zoneCache.RUnlock()
   163  	}
   164  	if !ok { // last resort
   165  		name = strconv.Itoa(zone)
   166  	}
   167  	return name
   168  }
   169  
   170  func (zc *ipv6ZoneCache) index(zone string) int {
   171  	updated := zoneCache.update(nil, false)
   172  	zoneCache.RLock()
   173  	index, ok := zoneCache.toIndex[zone]
   174  	zoneCache.RUnlock()
   175  	if !ok && !updated {
   176  		zoneCache.update(nil, true)
   177  		zoneCache.RLock()
   178  		index, ok = zoneCache.toIndex[zone]
   179  		zoneCache.RUnlock()
   180  	}
   181  	if !ok { // last resort
   182  		index, _ = strconv.Atoi(zone)
   183  	}
   184  	return index
   185  }