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