1-- POSIX's sockets in Landin types: TCP and UDP over IPv4 and IPv6, and
2-- Unix-domain sockets, which name a path and are this module's alone. An
3-- address is the system's own record, laid out here once for each
4-- system: Linux's begins with a 16-bit family, Darwin's and FreeBSD's
5-- with a length byte and a family byte, and each system numbers IPv6's
6-- family its own way. Every socket is closed on exec, as every
7-- descriptor this module makes is, and a send to a peer that has gone
8-- fails with `epipe` without raising SIGPIPE: Linux sends with
9-- MSG_NOSIGNAL, and Darwin and FreeBSD set SO_NOSIGPIPE on each socket.
10
11import core/io
12import core/text
13
14--- An IPv4 socket, AF_INET.
15public internet: atom
16--- An IPv6 socket, AF_INET6.
17public internet6: atom
18--- A Unix-domain socket, AF_UNIX, named by a path on this system.
19public local: atom
20--- A socket's family: `internet`, `internet6` or `local`.
21public socket_family: type = internet | internet6 | local
22
23--- A connected byte stream, SOCK_STREAM: TCP over IP.
24public stream: atom
25--- Datagrams, SOCK_DGRAM: UDP over IP.
26public datagram: atom
27--- A socket's kind: `stream` or `datagram`.
28public socket_kind: type = stream | datagram
29
30--- Stop receiving, SHUT_RD.
31public shut_reading: atom
32--- Stop sending, SHUT_WR: the peer reads the end of the stream.
33public shut_writing: atom
34--- Stop both, SHUT_RDWR.
35public shut_both: atom
36--- Which side `shutdown` stops.
37public shutdown_side: type = shut_reading | shut_writing | shut_both
38
39--- A socket address as the system lays it out, C's `struct
40--- sockaddr_storage` and the length of the part in use. Its fields belong
41--- to this module: make one with `internet_address` or `unix_address`, and
42--- read one with `family_of`, `address_of` and `unix_path`.
43public socket_address: type = struct
44 bytes: [128]u8
45 length: u32
46end socket_address
47
48--- Two descriptors of one connection, `socket_pair`'s: what is sent on
49--- either is received on the other.
50public connected_pair: type = struct
51 first: descriptor
52 second: descriptor
53end connected_pair
54
55fixed if compiler.os == linux then
56 -- The longest Unix-domain path, its NUL included: `sun_path`'s room.
57 unix_path_room: usize = 108
58
59 -- Linux's records begin with the family, a 16-bit number in the
60 -- host's order, which is little-endian everywhere Landin runs Linux.
61 put_family: (inout record: socket_address, family: socket_family)
62 -> none =
63 number: i32 = system_family_number(family)
64 record.bytes[0] = u8(number & 255)
65 record.bytes[1] = u8((number >> 8) & 255)
66 end put_family
67
68 family_number: (record: socket_address) -> (number: i32) =
69 number = i32(record.bytes[0]) | (i32(record.bytes[1]) << 8)
70 end family_number
71else
72 -- The longest Unix-domain path, its NUL included: `sun_path`'s room.
73 unix_path_room: usize = 104
74
75 -- Darwin's and FreeBSD's records begin with their own length, `sa_len`,
76 -- and then the family, a byte each.
77 put_family: (inout record: socket_address, family: socket_family)
78 -> none =
79 record.bytes[0] = u8(record.length)
80 record.bytes[1] = u8(system_family_number(family))
81 end put_family
82
83 family_number: (record: socket_address) -> (number: i32) =
84 number = i32(record.bytes[1])
85 end family_number
86end if
87
88-- Where a Unix-domain record's path begins on every system.
89unix_path_offset: usize = 2
90
91--- The system's record of the internet address `at`: C's `struct
92--- sockaddr_in`, 16 bytes, for an IPv4 address and `struct sockaddr_in6`,
93--- 28 bytes, for an IPv6 one, the port in network order and the IPv6 flow
94--- label and scope zero.
95public link(symbol: "_landin_posix_internet_address") internet_address:
96 (at: io.address) -> (made: socket_address) =
97 made = (bytes: zeroed, length: 16)
98 made.bytes[2] = u8(at.port >> 8)
99 made.bytes[3] = u8(at.port & 255)
100 if at.family == io.ipv4 then
101 put_family(made, internet)
102 for index in usize(0)..<4 do
103 made.bytes[4 + index] = at.octets[index]
104 end for
105 else
106 made.length = 28
107 put_family(made, internet6)
108 for index in usize(0)..<16 do
109 made.bytes[8 + index] = at.octets[index]
110 end for
111 end if
112end internet_address
113
114--- The system's record of the Unix-domain socket at `path`, C's `struct
115--- sockaddr_un`: `enametoolong` when the path and its NUL do not fit its
116--- 108 bytes on Linux or 104 on Darwin and FreeBSD, and `einval` for an
117--- empty path or one holding a NUL, which Linux would read as an abstract
118--- name.
119public link(symbol: "_landin_posix_unix_address") unix_address:
120 (path: text.host_string) -> (made: socket_address) ! errno =
121 bytes: []u8 = text.host_bytes(path)
122 fail einval when lenof bytes == 0
123 fail enametoolong when lenof bytes >= unix_path_room
124 made = (bytes: zeroed,
125 length: u32(unix_path_offset + lenof bytes + 1))
126 for index in usize(0)..<lenof bytes do
127 fail einval when bytes[index] == 0
128 made.bytes[unix_path_offset + index] = bytes[index]
129 end for
130 put_family(made, local)
131end unix_address
132
133--- The family of the socket address `at`: `eafnosupport` for one this
134--- module does not name.
135public link(symbol: "_landin_posix_family_of") family_of:
136 (at: socket_address) -> (family: socket_family) ! errno =
137 number: i32 = family_number(at)
138 family = if number == system_family_number(internet) then internet
139 elsif number == system_family_number(internet6) then internet6
140 elsif number == system_family_number(local) then local
141 else internet end if
142 fail eafnosupport when number <> system_family_number(family)
143end family_of
144
145--- The internet address and port the socket address `at` holds, its IPv6
146--- scope dropped: `eafnosupport` for a Unix-domain one, or a record too
147--- short for its family.
148public link(symbol: "_landin_posix_address_of") address_of:
149 (at: socket_address) -> (result: io.address) ! errno =
150 family: socket_family = try family_of(at)
151 port: u16 = (u16(at.bytes[2]) << 8) | u16(at.bytes[3])
152 result = io.unspecified(io.ipv4, port)
153 if family == internet then
154 fail eafnosupport when at.length < 8
155 result = io.ipv4_address(at.bytes[4], at.bytes[5], at.bytes[6],
156 at.bytes[7], port)
157 elsif family == internet6 then
158 fail eafnosupport when at.length < 24
159 result = io.unspecified(io.ipv6, port)
160 for index in usize(0)..<16 do
161 result.octets[index] = at.bytes[8 + index]
162 end for
163 else
164 fail eafnosupport
165 end if
166end address_of
167
168--- The path a Unix-domain socket address names, copied into `room` and
169--- lent from it: `eafnosupport` for an internet address, and `erange` when
170--- the path does not fit `room`. A socket bound to no path has an empty
171--- one.
172public link(symbol: "_landin_posix_unix_path") unix_path:
173 (at: socket_address, room: []mut u8)
174 -> (path: text.host_string from room) ! errno =
175 family: socket_family = try family_of(at)
176 fail eafnosupport when family <> local
177 mut length: usize = 0
178 mut limit: usize = usize(at.length)
179 if limit > 128 then limit = 128 end if
180 while unix_path_offset + length < limit
181 and at.bytes[unix_path_offset + length] <> 0 do
182 inc length
183 end while
184 fail erange when length > lenof room
185 for index in usize(0)..<length do
186 room[index] = at.bytes[unix_path_offset + index]
187 end for
188 path = text.host_over(room[0..<length])
189end unix_path
190
191--- Make a socket of `family` and `kind`, closed on exec and raising no
192--- SIGPIPE: `eafnosupport` when the system does not have the family, as
193--- one without IPv6 does not. On Darwin, which has no SOCK_CLOEXEC, the
194--- flag is set just after the socket is made, as `pipe`'s is.
195public link(symbol: "_landin_posix_socket") socket:
196 (family: socket_family, kind: socket_kind) -> (made: descriptor)
197 ! errno =
198 made = try system_socket(family, kind)
199end socket
200
201--- Make a pair of connected Unix-domain sockets of `kind`, each closed on
202--- exec and raising no SIGPIPE.
203public link(symbol: "_landin_posix_socket_pair") socket_pair:
204 (kind: socket_kind) -> (ends: connected_pair) ! errno =
205 ends = try system_socket_pair(kind)
206end socket_pair
207
208--- Bind `socket` to the address `at`: `eaddrinuse` when another socket
209--- holds it, `eaddrnotavail` when no interface here has it, `eacces` for a
210--- port the process may not take.
211public link(symbol: "_landin_posix_bind") bind:
212 (socket: descriptor, at: socket_address) -> none ! errno =
213 try system_bind(socket, addr at.bytes[0], at.length)
214end bind
215
216--- Make the bound stream socket `socket` a listener, with at most `backlog`
217--- connections waiting to be accepted, which each system caps at its own
218--- limit.
219public link(symbol: "_landin_posix_listen") listen:
220 (socket: descriptor, backlog: i32) -> none ! errno =
221 try system_listen(socket, backlog)
222end listen
223
224--- Take the next connection waiting on `listener` as a new socket, closed
225--- on exec and raising no SIGPIPE, waiting for one, or the errno: `eagain`
226--- when the listener's receive time limit passed first, `econnaborted` for
227--- a connection reset before it was taken. Linux limits the wait itself;
228--- Darwin and FreeBSD do not, so it is waited out here with `poll` for
229--- the limit `receive_timeout` reads. On Darwin the new socket is marked
230--- closed on exec just after it is made.
231public link(symbol: "_landin_posix_accept") accept:
232 (listener: descriptor) -> (accepted: descriptor) ! errno =
233 accepted = try system_accept(listener)
234end accept
235
236--- Connect `socket` to `to`: `econnrefused` when nothing listens there,
237--- `eintr` when a signal came first, after which the connection goes on and
238--- its outcome is what `poll` for `poll_output` and then `pending_error`
239--- say.
240public link(symbol: "_landin_posix_connect") connect:
241 (socket: descriptor, to: socket_address) -> none ! errno =
242 try system_connect(socket, addr to.bytes[0], to.length)
243end connect
244
245--- Send at most `lenof bytes` bytes on the connected `socket`: the count
246--- accepted, which a stream may find short, or the errno, `epipe` once
247--- the peer has gone, without SIGPIPE, and `eagain` when the send time
248--- limit passed.
249public link(symbol: "_landin_posix_send") send:
250 (socket: descriptor, bytes: []u8) -> (count: usize) ! errno =
251 count = try system_send(socket, bytes)
252end send
253
254--- Receive at most `lenof into` bytes from the connected `socket`: the
255--- count, zero once a stream's peer has finished sending, or the errno,
256--- `eagain` when the receive time limit passed and `econnreset` when the
257--- peer reset the connection.
258public link(symbol: "_landin_posix_receive") receive:
259 (socket: descriptor, into: []mut u8) -> (count: usize) ! errno =
260 count = try system_receive(socket, into)
261end receive
262
263--- Send `bytes` as one datagram to `to`: the count, which is all of them,
264--- or the errno, `emsgsize` for a datagram larger than the system sends.
265public link(symbol: "_landin_posix_send_to") send_to:
266 (socket: descriptor, bytes: []u8, to: socket_address)
267 -> (count: usize) ! errno =
268 count = try system_send_to(socket, bytes, addr to.bytes[0], to.length)
269end send_to
270
271--- What one `receive_from` took: the count, and the sender's address.
272public received: type = struct
273 count: usize
274 sender: socket_address
275end received
276
277--- Receive the next datagram into `into` and the address of its sender: a
278--- datagram longer than `into` is cut to it.
279public link(symbol: "_landin_posix_receive_from") receive_from:
280 (socket: descriptor, into: []mut u8) -> (result: received) ! errno =
281 result = (count: 0, sender: (bytes: zeroed, length: 128))
282 result.count = try system_receive_from(socket, into,
283 addr result.sender.bytes[0],
284 addr result.sender.length)
285end receive_from
286
287--- Stop receiving, sending or both on `socket`: `enotconn` when it is not
288--- connected.
289public link(symbol: "_landin_posix_shutdown") shutdown:
290 (socket: descriptor, side: shutdown_side) -> none ! errno =
291 how: i32 = match side
292 shut_reading: 0
293 shut_writing: 1
294 shut_both: 2
295 end match
296 try system_shutdown(socket, how)
297end shutdown
298
299--- The address `socket` is bound to, POSIX's `getsockname`.
300public link(symbol: "_landin_posix_socket_name") socket_name:
301 (socket: descriptor) -> (at: socket_address) ! errno =
302 at = (bytes: zeroed, length: 128)
303 try system_socket_name(socket, addr at.bytes[0], addr at.length)
304end socket_name
305
306--- The address of the peer `socket` is connected to, POSIX's
307--- `getpeername`: `enotconn` when there is none.
308public link(symbol: "_landin_posix_peer_name") peer_name:
309 (socket: descriptor) -> (at: socket_address) ! errno =
310 at = (bytes: zeroed, length: 128)
311 try system_peer_name(socket, addr at.bytes[0], addr at.length)
312end peer_name
313
314-- The socket options this module sets, each system numbering them its
315-- own way.
316reuse_address: atom
317no_delay: atom
318ipv6_only: atom
319receive_limit: atom
320send_limit: atom
321pending: atom
322socket_option: type = reuse_address | no_delay | ipv6_only | receive_limit
323 | send_limit | pending
324
325-- Set a flag option.
326flag_set: (socket: descriptor, option: socket_option, on: bool)
327 -> none ! errno =
328 value: i32 = if on then 1 else 0 end if
329 try system_set_number(socket, option, value)
330end flag_set
331
332--- Let the listener `socket` bind an address that earlier connections
333--- still hold in TIME_WAIT, `SO_REUSEADDR`, never one another listener
334--- holds. Set it before `bind`.
335public link(symbol: "_landin_posix_set_reuse_address") set_reuse_address:
336 (socket: descriptor, on: bool) -> none ! errno =
337 try flag_set(socket, reuse_address, on)
338end set_reuse_address
339
340--- Send a stream's small writes at once, `TCP_NODELAY`.
341public link(symbol: "_landin_posix_set_no_delay") set_no_delay:
342 (socket: descriptor, on: bool) -> none ! errno =
343 try flag_set(socket, no_delay, on)
344end set_no_delay
345
346--- Make an IPv6 socket take IPv6 alone, never IPv4 through mapped
347--- addresses, `IPV6_V6ONLY`, which Darwin and FreeBSD do from the start
348--- and Linux does not. Set it before `bind`.
349public link(symbol: "_landin_posix_set_ipv6_only") set_ipv6_only:
350 (socket: descriptor, on: bool) -> none ! errno =
351 try flag_set(socket, ipv6_only, on)
352end set_ipv6_only
353
354--- Limit how long a receive or an `accept` on `socket` waits,
355--- `SO_RCVTIMEO`, after which it fails `eagain`; a span of zero waits as
356--- long as it takes. The system keeps microseconds, so a part of one
357--- counts as a whole one, and Linux keeps its own ticks.
358public link(symbol: "_landin_posix_set_receive_timeout") set_receive_timeout:
359 (socket: descriptor, span: time_value) -> none ! errno =
360 fail einval when span.seconds < 0 or span.nanoseconds >= 1000000000
361 try system_set_time(socket, receive_limit, span)
362end set_receive_timeout
363
364--- Limit how long a send on `socket` waits, `SO_SNDTIMEO`, as
365--- `set_receive_timeout` limits a receive.
366public link(symbol: "_landin_posix_set_send_timeout") set_send_timeout:
367 (socket: descriptor, span: time_value) -> none ! errno =
368 fail einval when span.seconds < 0 or span.nanoseconds >= 1000000000
369 try system_set_time(socket, send_limit, span)
370end set_send_timeout
371
372--- How long a receive on `socket` waits, as `set_receive_timeout` set it,
373--- zero for no limit, to the microsecond: `unlisted` when the system gives
374--- back a record of another size, as QEMU's user emulation does.
375public link(symbol: "_landin_posix_receive_timeout") receive_timeout:
376 (socket: descriptor) -> (span: time_value) ! errno =
377 span = try system_get_time(socket, receive_limit)
378end receive_timeout
379
380--- Fail with the error pending on `socket`, `SO_ERROR`, clearing it, or
381--- return when there is none: how a connection `connect` began and a
382--- signal interrupted ended.
383public link(symbol: "_landin_posix_pending_error") pending_error:
384 (socket: descriptor) -> none ! errno =
385 number: i32 = try system_get_number(socket, pending)
386 fail errno_of(number) when number <> 0
387end pending_error
388
389-- A time value as microseconds, a part of one rounded up.
390microseconds_of: (span: time_value) -> (seconds: i64, microseconds: i64) =
391 seconds = span.seconds
392 microseconds = i64((span.nanoseconds + 999) / 1000)
393 if microseconds == 1000000 then
394 inc seconds
395 microseconds = 0
396 end if
397end microseconds_of