2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
24 type address := bytes;
26 fn address_family(a : address) : int;
28 fn socket(w : world, p s pr : int) : (world, handle);
29 fn connect(w : world, s : handle, addr : address) : world;
30 fn bind(w : world, s : handle, addr : address) : world;
31 fn listen(w : world, s : handle) : world;
32 fn accept(w : world, s : handle) : (world, handle);
33 fn getsockname(w : world, s : handle) : (world, address);
34 fn getpeername(w : world, s : handle) : (world, address);
35 fn recvfrom(w : world, s : handle, length : int, flags : int) : (world, bytes, address);
36 fn sendto(w : world, s : handle, data : bytes, flags : int, addr : address) : (world, int);
39 conversion fn sockopt_int(s : sockopt) : int;
40 conversion fn int_sockopt(i : int) : sockopt;
41 conversion fn sockopt_byte(s : sockopt) : byte;
42 conversion fn byte_sockopt(i : byte) : sockopt;
43 conversion fn sockopt_cstring(s : sockopt) : bytes;
44 conversion fn cstring_sockopt(b : bytes) : sockopt;
45 fn getsockopt(w : world, s : handle, l opt : int) : (world, sockopt);
46 fn setsockopt(w : world, s : handle, l opt : int, data : sockopt) : world;
48 fn getaddrinfo(w : world, name : bytes, port : int) : (world, list(address));
49 fn getnameinfo(w : world, addr : address) : (world, bytes);
51 fn connect_tcp(implicit w : world, host : bytes, port : int) : (world, handle);
57 fn address_family(a : address) : int
59 return a[0] + (a[1] shl 8);
62 fn socket(w : world, p s pr : int) : (world, handle)
66 pcode IO IO_Socket 2 4 0 =w2 =h w p s pr;
70 fn connect(w : world, s : handle, addr : address) : world
73 pcode IO IO_Connect 1 3 0 =w2 w s addr;
74 pcode IO IO_Connect_Wait 1 2 0 =w3 w2 s;
78 fn bind(w : world, s : handle, addr : address) : world
81 pcode IO IO_Bind 1 3 0 =w2 w s addr;
85 fn listen(w : world, s : handle) : world
88 pcode IO IO_Listen 1 2 0 =w2 w s;
92 fn accept(w : world, s : handle) : (world, handle)
96 pcode IO IO_Accept 2 2 0 =w2 =h w s;
100 fn getsockname(w : world, s : handle) : (world, address)
104 pcode IO IO_Get_Sock_Name 2 2 0 =w2 =addr w s;
108 fn getpeername(w : world, s : handle) : (world, address)
112 pcode IO IO_Get_Peer_Name 2 2 0 =w2 =addr w s;
116 fn recvfrom(w : world, s : handle, length : int, flags : int) : (world, bytes, address)
121 pcode IO IO_Recv_From 3 4 0 =w2 =data =addr w s length flags;
122 return w2, data, addr;
125 fn sendto(w : world, s : handle, data : bytes, flags : int, addr : address) : (world, int)
129 pcode IO IO_Send_To 2 5 0 =w2 =length w s data flags addr;
133 type sockopt := bytes;
135 conversion fn sockopt_int(s : sockopt) : int
137 return native_to_int(native.integer, s);
140 conversion fn int_sockopt(i : int) : sockopt
142 return int_to_native(native.integer, i);
145 conversion fn sockopt_byte(s : sockopt) : byte
150 conversion fn byte_sockopt(i : byte) : sockopt
155 conversion fn sockopt_cstring(s : sockopt) : bytes
157 if not list_ends_with(s, bytes.[ 0 ]) then
158 return exception_make_str(bytes, ec_sync, error_system_returned_invalid_data, 0, true, "the string returned by getsockopt is not 0-terminated");
159 return s[ .. len(s) - 1];
162 conversion fn cstring_sockopt(b : bytes) : sockopt
164 return b + bytes.[ 0 ];
167 fn getsockopt(w : world, s : handle, l opt : int) : (world, sockopt)
171 pcode IO IO_Get_Sock_Opt 2 4 0 =w2 =data w s l opt;
175 fn setsockopt(w : world, s : handle, l opt : int, data : sockopt) : world
178 pcode IO IO_Set_Sock_Opt 1 5 0 =w2 w s l opt data;
182 fn getaddrinfo(w : world, name : bytes, port : int) : (world, list(address))
186 pcode IO IO_Get_Addr_Info 2 3 0 =w2 =h w name port;
189 var s := empty(byte);
192 w3, b := read_partial~strict(w3, h, 1024);
193 if not len_greater_than(b, 0) then
199 var error_class := #100 * s[3] + s[2];
200 var error_type := #100 * s[5] + s[4];
201 var error_aux := #1000000 * s[9] + #10000 * s[8] + #100 * s[7] + s[6];
202 abort exception_make_str(int, error_class, error_type, error_aux, false, "failed to lookup " + name + ", " + ntos(port));
206 var a := empty(address);
207 while len_greater_than(s, 0) do [
208 var found_addrlen := #100 * s[1] + s[0];
209 var found_addr := s[2 .. 2 + found_addrlen];
210 s := s[2 + found_addrlen .. ];
217 fn getnameinfo(w : world, addr : address) : (world, bytes)
221 pcode IO IO_Get_Name_Info 2 2 0 =w2 =h w addr;
224 var s := empty(byte);
227 w3, b := read_partial~strict(w3, h, 1024);
228 if not len_greater_than(b, 0) then
234 var error_class := #100 * s[3] + s[2];
235 var error_type := #100 * s[5] + s[4];
236 var error_aux := #1000000 * s[9] + #10000 * s[8] + #100 * s[7] + s[6];
237 abort exception_make_str(int, error_class, error_type, error_aux, false, "failed to lookup address");
243 fn connect_recursive(w : world, a : list(address)) : (world, handle)
248 w1, s1 := socket(w1, address_family(a[0]), sock_stream, 0);
249 w1 := connect(w1, s1, a[0]);
251 if not len_greater_than(a, 1) then
254 w := sleep(w, 1000000);
258 w := select(b, w1, w);
260 if not is_exception w1 then
267 w2, s2 := connect_recursive(w2, a[1 .. ]);
270 w := select(b, w1, w2);
272 if not is_exception w1 then
276 if not is_exception w2 then
282 fn connect_tcp(implicit w : world, host : bytes, port : int) : (world, handle)
284 var a := getaddrinfo(host, port);
285 if not len_greater_than(a, 0) then
286 abort exception_make(int, ec_syscall, error_system, system_error_enetunreach, false);
287 return connect_recursive(a);