sys_posix.go raw

   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  }
 185