v / vlib / net / address.c.v
332 lines · 293 sloc · 7.16 KB · 03ffd49045f82086d8247bec1ee3f7418f80a43d
Raw
1module net
2
3import io.util
4import net.conv
5import os
6
7union AddrData {
8 Unix
9 Ip
10 Ip6
11}
12
13const addr_ip6_any = [16]u8{init: u8(0)}
14const addr_ip_any = [4]u8{init: u8(0)}
15
16fn set_addr_family(mut a Addr, family AddrFamily, sockaddr_size u32) {
17 unsafe {
18 // Addr starts with `len, f` on BSD/Darwin, but with `f` elsewhere.
19 $if macos || freebsd || openbsd || netbsd || dragonfly {
20 mut raw := &u8(&a)
21 raw[0] = u8(sockaddr_size)
22 raw[1] = u8(family)
23 } $else {
24 *(&u16(&a)) = u16(family)
25 }
26 }
27}
28
29// new_ip6 creates a new Addr from the IP6 address family, based on the given port and addr
30pub fn new_ip6(port u16, addr [16]u8) Addr {
31 n_port := conv.hton16(port)
32 mut a := Addr{
33 addr: AddrData{
34 Ip6: Ip6{
35 port: n_port
36 }
37 }
38 }
39 set_addr_family(mut a, .ip6, sizeof(C.sockaddr_in6))
40 unsafe { vmemcpy(&a.addr.Ip6.addr[0], &addr[0], 16) }
41 return a
42}
43
44// new_ip creates a new Addr from the IPv4 address family, based on the given port and addr
45pub fn new_ip(port u16, addr [4]u8) Addr {
46 n_port := conv.hton16(port)
47 mut a := Addr{
48 addr: AddrData{
49 Ip: Ip{
50 port: n_port
51 }
52 }
53 }
54 set_addr_family(mut a, .ip, sizeof(C.sockaddr_in))
55 unsafe { vmemcpy(&a.addr.Ip.addr[0], &addr[0], 4) }
56 return a
57}
58
59fn temp_unix() !Addr {
60 // create a temp file to get a filename
61 // close it
62 // remove it
63 // then reuse the filename
64 mut file, filename := util.temp_file()!
65 file.close()
66 os.rm(filename)!
67 addrs := resolve_addrs(filename, .unix, .udp)!
68 return addrs[0]
69}
70
71// family returns the family/kind of the given address `a`
72pub fn (a Addr) family() AddrFamily {
73 return unsafe { AddrFamily(a.f) }
74}
75
76// port returns the ip or ip6 port of the given address `a`
77pub fn (a Addr) port() !u16 {
78 match unsafe { AddrFamily(a.f) } {
79 .ip {
80 unsafe {
81 return conv.ntoh16(a.addr.Ip.port)
82 }
83 }
84 .ip6 {
85 unsafe {
86 return conv.ntoh16(a.addr.Ip6.port)
87 }
88 }
89 .unix {
90 return error('unix addr has no port')
91 }
92 .unspec {
93 return error('cannot find port for unspec addr family')
94 }
95 }
96}
97
98const max_ip_len = 24
99const max_ip6_len = 46
100
101// str returns a string representation of `a`
102pub fn (a Ip) str() string {
103 buf := [max_ip_len]char{}
104
105 res := &char(C.inet_ntop(i32(AddrFamily.ip), &a.addr, &buf[0], buf.len))
106
107 if res == 0 {
108 return '<Unknown>'
109 }
110
111 saddr := unsafe { cstring_to_vstring(res) }
112 port := conv.ntoh16(a.port)
113 return '${saddr}:${port}'
114}
115
116// str returns a string representation of `a`. The IPv6 portion is
117// rendered per RFC 5952 by canonical_ipv6_from_bytes (pure V), instead
118// of libc's inet_ntop, which historically emits the deprecated
119// IPv4-compatible mixed form (`::a.b.c.d`) for any address with the
120// upper 96 bits zero.
121pub fn (a Ip6) str() string {
122 saddr := canonical_ipv6_from_bytes(a.addr[..]) or { return '<Unknown>' }
123 port := conv.ntoh16(a.port)
124 return '[${saddr}]:${port}'
125}
126
127const aoffset = __offsetof(Addr, addr)
128
129// len returns the length in bytes of the address `a`, depending on its family
130pub fn (a &Addr) len() u32 {
131 match a.family() {
132 .ip {
133 return sizeof(Ip) + aoffset
134 }
135 .ip6 {
136 return sizeof(Ip6) + aoffset
137 }
138 .unix {
139 return sizeof(Unix) + aoffset
140 }
141 else {
142 panic('Unknown address family')
143 }
144 }
145}
146
147// resolve_addrs converts the given `addr`, `family` and `typ` to a list of addresses
148pub fn resolve_addrs(addr string, family AddrFamily, typ SocketType) ![]Addr {
149 match family {
150 .ip, .ip6, .unspec {
151 return resolve_ipaddrs(addr, family, typ)
152 }
153 .unix {
154 resolved := Unix{}
155
156 if addr.len > max_unix_path {
157 return error('net: resolve_addrs Unix socket address is too long')
158 }
159
160 // Copy the unix path into the address struct
161 unsafe {
162 C.memcpy(&resolved.path, addr.str, addr.len)
163 }
164
165 return [
166 Addr{
167 f: u8(AddrFamily.unix)
168 addr: AddrData{
169 Unix: resolved
170 }
171 },
172 ]
173 }
174 }
175}
176
177// resolve_addrs converts the given `addr` and `typ` to a list of addresses
178pub fn resolve_addrs_fuzzy(addr string, typ SocketType) ![]Addr {
179 if addr.len == 0 {
180 return error('none')
181 }
182
183 // Use a small heuristic to figure out what address family this is
184 // (out of the ones that we support)
185
186 if addr.contains(':') {
187 // Colon is a reserved character in unix paths
188 // so this must be an ip address
189 return resolve_addrs(addr, .unspec, typ)
190 }
191
192 return resolve_addrs(addr, .unix, typ)
193}
194
195fn wrap_getaddrinfo_error(code int) ! {
196 if code == 0 {
197 return
198 }
199 $if windows {
200 socket_error(0 - code)!
201 } $else {
202 if code == C.EAI_SYSTEM {
203 err_code := error_code()
204 return error_with_code('net: getaddrinfo failed: ${os.posix_get_error_msg(err_code)}',
205 err_code)
206 }
207 return error_with_code('net: getaddrinfo failed: ${unsafe { cstring_to_vstring(C.gai_strerror(code)) }}',
208 code)
209 }
210}
211
212// resolve_ipaddrs converts the given `addr`, `family` and `typ` to a list of addresses
213pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ![]Addr {
214 address, port := split_address(addr)!
215
216 if addr[0] == `:` {
217 match family {
218 .ip6 {
219 return [new_ip6(port, addr_ip6_any)]
220 }
221 .ip, .unspec {
222 return [new_ip(port, addr_ip_any)]
223 }
224 else {}
225 }
226 }
227
228 mut hints := C.addrinfo{
229 // ai_family: int(family)
230 // ai_socktype: int(typ)
231 // ai_flags: C.AI_PASSIVE
232 }
233 unsafe { vmemset(&hints, 0, int(sizeof(hints))) }
234 hints.ai_family = int(family)
235 hints.ai_socktype = int(typ)
236 hints.ai_flags = C.AI_PASSIVE
237
238 results := &C.addrinfo(unsafe { nil })
239
240 sport := '${port}'
241
242 wrap_getaddrinfo_error(C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &results))!
243
244 defer {
245 C.freeaddrinfo(results)
246 }
247
248 // Now that we have our linked list of addresses
249 // convert them into an array
250 mut addresses := []Addr{}
251
252 for result := unsafe { results }; !isnil(result); result = result.ai_next {
253 match unsafe { AddrFamily(result.ai_family) } {
254 .ip {
255 new_addr := Addr{
256 addr: AddrData{
257 Ip: Ip{}
258 }
259 }
260 unsafe {
261 C.memcpy(&new_addr, result.ai_addr, result.ai_addrlen)
262 }
263 addresses << new_addr
264 }
265 .ip6 {
266 new_addr := Addr{
267 addr: AddrData{
268 Ip6: Ip6{}
269 }
270 }
271 unsafe {
272 C.memcpy(&new_addr, result.ai_addr, result.ai_addrlen)
273 }
274 addresses << new_addr
275 }
276 else {
277 panic('Unexpected address family ' + result.ai_family.str())
278 }
279 }
280 }
281
282 return addresses
283}
284
285// str returns a string representation of the address `a`
286pub fn (a Addr) str() string {
287 match unsafe { AddrFamily(a.f) } {
288 .ip {
289 unsafe {
290 return a.addr.Ip.str()
291 }
292 }
293 .ip6 {
294 unsafe {
295 return a.addr.Ip6.str()
296 }
297 }
298 .unix {
299 unsafe {
300 return tos_clone(a.addr.Unix.path[0..max_unix_path].data)
301 }
302 }
303 .unspec {
304 return '<.unspec>'
305 }
306 }
307}
308
309// addr_from_socket_handle returns an address, based on the given integer socket `handle`
310pub fn addr_from_socket_handle(handle int) Addr {
311 mut addr := Addr{
312 addr: AddrData{
313 Ip6: Ip6{}
314 }
315 }
316 mut size := sizeof(addr)
317 C.getsockname(handle, voidptr(&addr), &size)
318 return addr
319}
320
321// peer_addr_from_socket_handle retrieves the ip address and port number, given a socket handle
322pub fn peer_addr_from_socket_handle(handle int) !Addr {
323 mut addr := Addr{
324 addr: AddrData{
325 Ip6: Ip6{}
326 }
327 }
328 mut size := sizeof(Addr)
329 socket_error_message(C.getpeername(handle, voidptr(&addr), &size),
330 'peer_addr_from_socket_handle failed')!
331 return addr
332}
333