Landin library reference discuss source

platform/posix/sockets.ldn

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