github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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 }