You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

184 lines
4.6 KiB

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