hosted/system/network.ldn
1-- The hosted provider's `net.network`: a socket is a descriptor of
2-- platform/posix's, a file of this provider like any other, so `io.read`
3-- and `io.write` take a connection as they take a file. Written once,
4-- over platform/posix, like the rest.
5
6import core/io
7import core/net
8import core/time
9import platform/posix
10
11-- How many connections may wait to be accepted, which each system caps at
12-- its own limit: the BSDs' SOMAXCONN.
13backlog: i32 = 128
14
15-- The family of socket an address needs.
16family_of: (at: net.address) -> (family: posix.socket_family) =
17 family = if at.family == net.ipv4 then posix.internet
18 else posix.internet6 end if
19end family_of
20
21-- Whether an errno says the system does not have the address here: no
22-- interface holds it, or the family is missing altogether.
23unavailable: (problem: posix.errno) -> (yes: bool) =
24 yes = problem == posix.eaddrnotavail or problem == posix.eafnosupport
25 or problem == posix.epfnosupport
26end unavailable
27
28-- Whether an errno says the peer has gone: it closed or reset the
29-- connection, or the system gave up on it.
30gone: (problem: posix.errno) -> (yes: bool) =
31 yes = problem == posix.epipe or problem == posix.econnreset
32 or problem == posix.etimedout or problem == posix.enotconn
33end gone
34
35-- A socket bound to `at`, a listener when `listening`: an IPv6 socket
36-- takes IPv6 alone on every system, and a listener may take an address
37-- its earlier connections still hold.
38bound: (at: net.address, kind: posix.socket_kind, listening: bool)
39 -> (socket: posix.descriptor) ! posix.errno =
40 socket = try posix.socket(family_of(at), kind)
41 undo quietly_closed(socket)
42 if at.family == net.ipv6 then
43 try posix.set_ipv6_only(socket, true)
44 end if
45 if listening then
46 try posix.set_reuse_address(socket, true)
47 end if
48 try posix.bind(socket, posix.internet_address(at))
49 if listening then
50 try posix.listen(socket, backlog)
51 end if
52end bound
53
54-- The errno a binding failed with, as the concept's atoms.
55bind_failure: (self: host, problem: posix.errno)
56 -> none ! net.address_in_use | net.address_unavailable
57 | io.no_access | io.io_failed =
58 record(self, problem)
59 fail net.address_in_use when problem == posix.eaddrinuse
60 fail net.address_unavailable when unavailable(problem)
61 fail io.no_access when refused(problem)
62 fail io.io_failed
63end bind_failure
64
65system_listen: (self: host, at: net.address) -> (listener: io.file)
66 ! net.address_in_use | net.address_unavailable | io.no_access
67 | io.io_failed =
68 self.failure.val = 0
69 made: posix.descriptor = bound(at, posix.stream, true) else (problem)
70 try bind_failure(self, problem)
71 fail io.io_failed
72 end
73 listener = socket_of(made)
74end system_listen
75
76system_open_datagram: (self: host, at: net.address) -> (socket: io.file)
77 ! net.address_in_use | net.address_unavailable
78 | io.no_access | io.io_failed =
79 self.failure.val = 0
80 made: posix.descriptor = bound(at, posix.datagram, false) else (problem)
81 try bind_failure(self, problem)
82 fail io.io_failed
83 end
84 socket = socket_of(made)
85end system_open_datagram
86
87-- A connection a signal interrupted goes on without its caller: wait
88-- until it is writable, which it is once it has ended either way, and
89-- take its outcome from the socket.
90finished: (socket: posix.descriptor) -> none ! posix.errno =
91 mut entries: [1]posix.poll_entry =
92 [posix.watch(socket, posix.poll_output)]
93 loop do
94 _ = posix.poll(entries[0..<1], -1) else (problem)
95 continue when problem == posix.eintr
96 fail problem
97 end
98 break
99 end loop
100 try posix.pending_error(socket)
101end finished
102
103connected: (to: net.address) -> (socket: posix.descriptor) ! posix.errno =
104 socket = try posix.socket(family_of(to), posix.stream)
105 undo quietly_closed(socket)
106 posix.connect(socket, posix.internet_address(to)) else (problem)
107 fail problem when problem <> posix.eintr
108 try finished(socket)
109 end
110end connected
111
112system_connect: (self: host, to: net.address) -> (opened: io.file)
113 ! net.refused | net.unreachable | net.address_unavailable
114 | net.timed_out | io.io_failed =
115 self.failure.val = 0
116 made: posix.descriptor = connected(to) else (problem)
117 record(self, problem)
118 fail net.refused when problem == posix.econnrefused
119 fail net.unreachable when problem == posix.enetunreach
120 or problem == posix.ehostunreach
121 fail net.address_unavailable when unavailable(problem)
122 fail net.timed_out when problem == posix.etimedout
123 fail io.io_failed
124 end
125 opened = socket_of(made)
126end system_connect
127
128system_accept: (self: host, listener: io.file) -> (accepted: io.file)
129 ! net.timed_out | io.io_failed =
130 self.failure.val = 0
131 loop do
132 made: posix.descriptor =
133 posix.accept(descriptor_of(listener)) else (problem)
134 -- An interrupted accept took nothing, and a connection
135 -- its peer reset before it was taken is no connection.
136 continue when problem == posix.eintr
137 or problem == posix.econnaborted
138 record(self, problem)
139 fail net.timed_out when problem == posix.eagain
140 fail io.io_failed
141 end
142 accepted = socket_of(made)
143 return
144 end loop
145end system_accept
146
147system_send: (self: host, connection: io.file, bytes: []u8)
148 -> none ! net.disconnected | net.timed_out | io.io_failed =
149 self.failure.val = 0
150 mut sent: usize = 0
151 while sent < lenof bytes do
152 -- A send interrupted before it sent anything is made again with
153 -- what is left; a short one goes on from where it stopped.
154 accepted: usize =
155 posix.send(descriptor_of(connection),
156 bytes[sent..<lenof bytes]) else (problem)
157 continue when problem == posix.eintr
158 record(self, problem)
159 fail net.disconnected when gone(problem)
160 fail net.timed_out when problem == posix.eagain
161 fail io.io_failed
162 end
163 fail io.io_failed when accepted == 0
164 or accepted > lenof bytes - sent
165 sent += accepted
166 end while
167end system_send
168
169system_receive: (self: host, connection: io.file, into: []mut u8)
170 -> (count: usize)
171 ! net.disconnected | net.timed_out | io.io_failed =
172 self.failure.val = 0
173 count = 0
174 return when lenof into == 0
175 loop do
176 got: usize = posix.receive(descriptor_of(connection), into)
177 else (problem)
178 continue when problem == posix.eintr
179 record(self, problem)
180 fail net.disconnected when gone(problem)
181 fail net.timed_out when problem == posix.eagain
182 fail io.io_failed
183 end
184 fail io.io_failed when got > lenof into
185 count = got
186 return
187 end loop
188end system_receive
189
190system_send_to: (self: host, socket: io.file, bytes: []u8,
191 to: net.address) -> none
192 ! net.unreachable | net.timed_out | io.io_failed =
193 self.failure.val = 0
194 loop do
195 accepted: usize =
196 posix.send_to(descriptor_of(socket), bytes,
197 posix.internet_address(to)) else (problem)
198 continue when problem == posix.eintr
199 record(self, problem)
200 fail net.unreachable when problem == posix.enetunreach
201 or problem == posix.ehostunreach
202 fail net.timed_out when problem == posix.eagain
203 fail io.io_failed
204 end
205 -- A datagram goes whole or not at all.
206 fail io.io_failed when accepted <> lenof bytes
207 return
208 end loop
209end system_send_to
210
211system_receive_from: (self: host, socket: io.file, into: []mut u8)
212 -> (taken: net.datagram) ! net.timed_out | io.io_failed =
213 self.failure.val = 0
214 loop do
215 got: posix.received =
216 posix.receive_from(descriptor_of(socket), into) else (problem)
217 continue when problem == posix.eintr
218 record(self, problem)
219 fail net.timed_out when problem == posix.eagain
220 fail io.io_failed
221 end
222 fail io.io_failed when got.count > lenof into
223 sender: net.address = posix.address_of(got.sender) else (problem)
224 record(self, problem)
225 fail io.io_failed
226 end
227 taken = (count: got.count, sender: sender)
228 return
229 end loop
230end system_receive_from
231
232system_shutdown: (self: host, connection: io.file,
233 stopping: net.direction) -> none ! io.io_failed =
234 self.failure.val = 0
235 side: posix.shutdown_side = match stopping
236 net.receiving: posix.shut_reading
237 net.sending: posix.shut_writing
238 net.both_directions: posix.shut_both
239 end match
240 posix.shutdown(descriptor_of(connection), side) else (problem)
241 record(self, problem)
242 fail io.io_failed
243 end
244end system_shutdown
245
246-- The internet address of a socket's own end, or with `peer` of the
247-- other end.
248named: (socket: io.file, peer: bool) -> (at: net.address) ! posix.errno =
249 record: posix.socket_address =
250 if peer then try posix.peer_name(descriptor_of(socket))
251 else try posix.socket_name(descriptor_of(socket)) end if
252 at = try posix.address_of(record)
253end named
254
255system_local_address: (self: host, socket: io.file) -> (at: net.address)
256 ! io.io_failed =
257 self.failure.val = 0
258 at = named(socket, false) else (problem)
259 record(self, problem)
260 fail io.io_failed
261 end
262end system_local_address
263
264system_peer_address: (self: host, connection: io.file)
265 -> (at: net.address) ! io.io_failed =
266 self.failure.val = 0
267 at = named(connection, true) else (problem)
268 record(self, problem)
269 fail io.io_failed
270 end
271end system_peer_address
272
273-- A span as the system's seconds and nanoseconds.
274time_of: (span: time.duration) -> (reading: posix.time_value) =
275 whole: u64 = u64(span) / 1000000000
276 reading = (seconds: i64(whole),
277 nanoseconds: u32(u64(span) - whole * 1000000000))
278end time_of
279
280limited: (socket: io.file, receive_limit: time.duration,
281 send_limit: time.duration) -> none ! posix.errno =
282 try posix.set_receive_timeout(descriptor_of(socket),
283 time_of(receive_limit))
284 try posix.set_send_timeout(descriptor_of(socket), time_of(send_limit))
285end limited
286
287system_set_timeouts: (self: host, socket: io.file,
288 receive_limit: time.duration, send_limit: time.duration)
289 -> none ! io.io_failed =
290 self.failure.val = 0
291 limited(socket, receive_limit, send_limit) else (problem)
292 record(self, problem)
293 fail io.io_failed
294 end
295end system_set_timeouts
296
297system_set_no_delay: (self: host, connection: io.file, on: bool)
298 -> none ! io.io_failed =
299 self.failure.val = 0
300 posix.set_no_delay(descriptor_of(connection), on) else (problem)
301 record(self, problem)
302 fail io.io_failed
303 end
304end system_set_no_delay
305
306-- A failed close may already have released the descriptor, as a file's
307-- may, so it is never retried.
308system_close_socket: (self: host, sink socket: io.file)
309 -> none ! io.io_failed =
310 self.failure.val = 0
311 posix.close(descriptor_of(socket)) else (problem)
312 record(self, problem)
313 fail io.io_failed
314 end
315end system_close_socket
316
317host is net.network
318 (connect: system_connect, listen: system_listen,
319 accept: system_accept, open_datagram: system_open_datagram,
320 send: system_send, receive: system_receive, send_to: system_send_to,
321 receive_from: system_receive_from, shutdown: system_shutdown,
322 local_address: system_local_address,
323 peer_address: system_peer_address,
324 set_timeouts: system_set_timeouts, set_no_delay: system_set_no_delay,
325 close_socket: system_close_socket)