1 | module net |
2 | |
3 | import io.util |
4 | import net.conv |
5 | import os |
6 | |
7 | union AddrData { |
8 | Unix |
9 | Ip |
10 | Ip6 |
11 | } |
12 | |
13 | const ( |
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 |
19 | pub 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 |
41 | pub 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 | |
62 | fn 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` |
75 | pub fn (a Addr) family() AddrFamily { |
76 | return unsafe { AddrFamily(a.f) } |
77 | } |
78 | |
79 | const ( |
80 | max_ip_len = 24 |
81 | max_ip6_len = 46 |
82 | ) |
83 | |
84 | // str returns a string representation of `a` |
85 | pub 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` |
105 | pub 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 | |
124 | const aoffset = __offsetof(Addr, addr) |
125 | |
126 | // len returns the length in bytes of the address `a`, depending on its family |
127 | pub 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 |
145 | pub 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 |
175 | pub 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 |
193 | pub 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` |
272 | pub 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` |
296 | pub 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 |
308 | pub 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 | } |