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