1
2
3
4
5
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
20
21
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
107
108
109
110
111
112 type ipv6ZoneCache struct {
113 sync.RWMutex
114 lastFetched time.Time
115 toIndex map[string]int
116 toName map[int]string
117 }
118
119 var zoneCache = ipv6ZoneCache{
120 toIndex: make(map[string]int),
121 toName: make(map[int]string),
122 }
123
124
125
126
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 {
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 {
181 index, _ = strconv.Atoi(zone)
182 }
183 return index
184 }
185
View as plain text