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
7package socket
8
9import (
10	"encoding/binary"
11	"errors"
12	"net"
13	"runtime"
14	"strconv"
15	"sync"
16	"time"
17)
18
19func 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
32func 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
65func 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.
111type 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
118var 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.
126func (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
151func (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
168func (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}
184