blob: 9a0dbcfb98f412b58c0cd703d2d7cde15aafb185 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001// 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 go1.9
6// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
7
8package socket
9
10import (
11 "encoding/binary"
12 "errors"
13 "net"
14 "runtime"
15 "strconv"
16 "sync"
17 "time"
18)
19
20func marshalInetAddr(a net.Addr) []byte {
21 switch a := a.(type) {
22 case *net.TCPAddr:
23 return marshalSockaddr(a.IP, a.Port, a.Zone)
24 case *net.UDPAddr:
25 return marshalSockaddr(a.IP, a.Port, a.Zone)
26 case *net.IPAddr:
27 return marshalSockaddr(a.IP, 0, a.Zone)
28 default:
29 return nil
30 }
31}
32
33func marshalSockaddr(ip net.IP, port int, zone string) []byte {
34 if ip4 := ip.To4(); ip4 != nil {
35 b := make([]byte, sizeofSockaddrInet)
36 switch runtime.GOOS {
37 case "linux", "solaris", "windows":
38 NativeEndian.PutUint16(b[:2], uint16(sysAF_INET))
39 default:
40 b[0] = sizeofSockaddrInet
41 b[1] = sysAF_INET
42 }
43 binary.BigEndian.PutUint16(b[2:4], uint16(port))
44 copy(b[4:8], ip4)
45 return b
46 }
47 if ip6 := ip.To16(); ip6 != nil && ip.To4() == nil {
48 b := make([]byte, sizeofSockaddrInet6)
49 switch runtime.GOOS {
50 case "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 b
62 }
63 return nil
64}
65
66func 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 "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) < sizeofSockaddrInet {
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.
112type 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
119var zoneCache = ipv6ZoneCache{
120 toIndex: make(map[string]int),
121 toName: make(map[int]string),
122}
123
124func (zc *ipv6ZoneCache) update(ift []net.Interface) {
125 zc.Lock()
126 defer zc.Unlock()
127 now := time.Now()
128 if zc.lastFetched.After(now.Add(-60 * time.Second)) {
129 return
130 }
131 zc.lastFetched = now
132 if len(ift) == 0 {
133 var err error
134 if ift, err = net.Interfaces(); err != nil {
135 return
136 }
137 }
138 zc.toIndex = make(map[string]int, len(ift))
139 zc.toName = make(map[int]string, len(ift))
140 for _, ifi := range ift {
141 zc.toIndex[ifi.Name] = ifi.Index
142 if _, ok := zc.toName[ifi.Index]; !ok {
143 zc.toName[ifi.Index] = ifi.Name
144 }
145 }
146}
147
148func (zc *ipv6ZoneCache) name(zone int) string {
149 zoneCache.update(nil)
150 zoneCache.RLock()
151 defer zoneCache.RUnlock()
152 name, ok := zoneCache.toName[zone]
153 if !ok {
154 name = strconv.Itoa(zone)
155 }
156 return name
157}
158
159func (zc *ipv6ZoneCache) index(zone string) int {
160 zoneCache.update(nil)
161 zoneCache.RLock()
162 defer zoneCache.RUnlock()
163 index, ok := zoneCache.toIndex[zone]
164 if !ok {
165 index, _ = strconv.Atoi(zone)
166 }
167 return index
168}