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  }