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