1#pragma once
2
3#include <arpa/inet.h>
4#include <sys/socket.h>
5
6#include <pw.h>
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12/****************************************************************
13 * Status codes
14 */
15
16extern uint16_t PweBadAddressFamily;
17extern uint16_t PweBadIpAddress;
18extern uint16_t PweBadPort;
19extern uint16_t PweHostAddressExpected;
20extern uint16_t PweAddressFamilyMismatch;
21extern uint16_t PweSocketNameTooLong;
22extern uint16_t PweMissingNetmask;
23extern uint16_t PweBadNetmask;
24extern uint16_t PwePortUnspecified;
25
26
27/****************************************************************
28 * SockAddr type
29 */
30
31extern uint16_t PwTypeId_SockAddr;
32
33#define pw_is_sockaddr(value) pw_is_subtype((value), PwTypeId_SockAddr)
34
35PW_STRUCT(_PwSockAddrData) {
36 struct sockaddr_storage addr;
37 unsigned netmask; // netmask in CIDR notation, i.e. number of network bits;
38 // nonzero for subnet address, zero for host address
39};
40
41// helper functions
42
43static inline unsigned pw_sockaddr_netmask(PwValuePtr value)
44/*
45 * return `netmask` (number of bits).
46 */
47{
48 return ((_PwSockAddrData*) _pw_get_struct_ptr(value, PwTypeId_SockAddr))->netmask;
49}
50
51static inline int pw_sockaddr_family(PwValuePtr value)
52/*
53 * return address family (domain in terms of socket() args)
54 */
55{
56 return ((_PwSockAddrData*) _pw_get_struct_ptr(value, PwTypeId_SockAddr))->addr.ss_family;
57}
58
59static inline in_port_t pw_sockaddr_port(PwValuePtr value)
60/*
61 * return port in host byte order
62 */
63{
64 _PwSockAddrData* sa = _pw_get_struct_ptr(value, PwTypeId_SockAddr);
65 // port is at the same location for both ipv4 and ipv6, thus using sockaddr_in
66 return ntohs(((struct sockaddr_in*) &sa->addr)->sin_port);
67}
68
69static inline unsigned pw_sockaddr_ipv4(PwValuePtr value)
70/*
71 * return IPv4 address in host byte order
72 */
73{
74 _PwSockAddrData* sa = _pw_get_struct_ptr(value, PwTypeId_SockAddr);
75 pw_hard_assert(sa->addr.ss_family == AF_INET);
76 return ntohl(((struct sockaddr_in*) &sa->addr)->sin_addr.s_addr);
77}
78
79static inline uint8_t* pw_sockaddr_ipv6(PwValuePtr value)
80/*
81 * Return pointer to IPv6 address in network byte order.
82 * The result should be used within lifetime of `value`.
83 */
84{
85 _PwSockAddrData* sa = _pw_get_struct_ptr(value, PwTypeId_SockAddr);
86 pw_hard_assert(sa->addr.ss_family == AF_INET6);
87 return ((struct sockaddr_in6*) &sa->addr)->sin6_addr.s6_addr;
88}
89
90/****************************************************************
91 * Socket interface.
92 */
93
94extern uint16_t PwInterfaceId_Socket;
95
96PW_METHOD_BEGIN(Socket, bind)
97 bool (*PwFunc_Socket_bind)(PwMethod_Socket_bind* mthis, PwValuePtr self, PwValuePtr local_addr)
98PW_METHOD_END(Socket, bind)
99/*
100 * Set `local_addr` in _PwSocket structure and call `bind` function.
101 *
102 * `local_addr` can be one of:
103 * - SockAddr with netmask equals to zero and address family matching socket's domain;
104 * - String, either parseable as IP address:port or local socket name.
105 *
106 * Local socket names are up to sizeof(sockaddr_un.sun_path)-1 characters.
107 * If the string starts with null character, it is treated
108 * as a name in Linux abstract namespace.
109 */
110
111PW_METHOD_BEGIN(Socket, reuse_addr)
112 bool (*PwFunc_Socket_reuse_addr)(PwMethod_Socket_reuse_addr* mthis, PwValuePtr self, bool reuse)
113PW_METHOD_END(Socket, reuse_addr)
114/*
115 * Set or clear SO_REUSEADDR option for socket.
116 */
117
118PW_METHOD_BEGIN(Socket, listen)
119 bool (*PwFunc_Socket_listen)(PwMethod_Socket_listen* mthis, PwValuePtr self, int backlog, uint16_t new_sock_type)
120PW_METHOD_END(Socket, listen)
121/*
122 * Call `listen`, set listen_backlog
123 *
124 * If backlog is 0, it is set to 5.
125 *
126 * `new_sock_type` is the type of socket created for incoming connections.
127 * If it is PwTypeId_Null, use the type of listening socket.
128 */
129
130PW_METHOD_BEGIN(Socket, is_listening)
131 bool (*PwFunc_Socket_is_listening)(PwMethod_Socket_is_listening* mthis, PwValuePtr self, bool* result)
132PW_METHOD_END(Socket, is_listening)
133/*
134 * Write listening status of socket to `result`
135 *
136 * Always return true.
137 */
138
139PW_METHOD_BEGIN(Socket, accept)
140 bool (*PwFunc_Socket_accept)(PwMethod_Socket_accept* mthis, PwValuePtr self, PwValuePtr result)
141PW_METHOD_END(Socket, accept)
142/*
143 * Accept incoming connection on listening socket
144 * and return new socket with `remote_addr` initialized
145 * in _PwSocket structure.
146 *
147 * The type of returned socket is same as self,
148 * which means that AsyncSocket is returned if self is AsyncSocket.
149 */
150
151PW_METHOD_BEGIN(Socket, connect)
152 bool (*PwFunc_Socket_connect)(PwMethod_Socket_connect* mthis, PwValuePtr self, PwValuePtr remote_addr)
153PW_METHOD_END(Socket, connect)
154/*
155 * Set `remote_addr` in _PwSocket structure and call `connect` function.
156 *
157 * `remote_addr` can be one of:
158 * - SockAddr with netmask equals to zero and address family matching socket's domain;
159 * - String, either parseable as IP address:port or local socket name.
160 *
161 * Host names must be explicitly resolved before calling this method.
162 * If address family is not AF_LOCAL and string contains host name,
163 * PweBadIpAddress is returned.
164 *
165 * Return EINPROGRESS errno for both local and inet non-blocking sockets
166 * (originally errnos are different, see man page).
167 */
168
169PW_METHOD_BEGIN(Socket, shutdown)
170 bool (*PwFunc_Socket_shutdown)(PwMethod_Socket_shutdown* mthis, PwValuePtr self, int how)
171PW_METHOD_END(Socket, shutdown)
172/*
173 * Call `shutdown` function.
174 */
175
176PW_METHOD_BEGIN(Socket, get_socket_error)
177 bool (*PwFunc_Socket_get_socket_error)(PwMethod_Socket_get_socket_error* mthis, PwValuePtr self, PwValuePtr result)
178PW_METHOD_END(Socket, get_socket_error)
179/*
180 * Write success to the result, or PwStatus_SystemError containing result of getsockopt(SOL_SOCKET, SO_ERROR)
181 * If getsockopt fails, return false.
182 */
183
184#define PW_SOCKET_INTERFACE_METHODS \
185 X(bind, 1) \
186 X(reuse_addr, 1) \
187 X(listen, 1) \
188 X(is_listening, 1) \
189 X(accept, 1) \
190 X(connect, 1) \
191 X(shutdown, 1) \
192 X(get_socket_error)
193 // TODO get/setsockopt
194 // TODO datagram functions: sendto, recvfrom
195
196PW_INTERFACE_BEGIN(Socket)
197#define X(name, ...) PwMethod_Socket_##name name;
198 PW_SOCKET_INTERFACE_METHODS
199#undef X
200PW_INTERFACE_END(Socket)
201
202
203/****************************************************************
204 * Socket data type.
205 *
206 * Socket data type supports the following interfaces:
207 * - Socket
208 * - Reader
209 * - Writer
210 */
211
212extern uint16_t PwTypeId_Socket;
213
214#define pw_is_socket(value) pw_is_subtype((value), PwTypeId_Socket)
215
216// constructor args
217PW_STRUCT(PwSocketCtorArgs) {
218 PwCtorArgs* next;
219 uint16_t type_id;
220
221 int domain;
222 int type;
223 int protocol;
224
225};
226
227/****************************************************************
228 * Shorthand functions
229 */
230
231[[nodiscard]] static inline bool pw_socket(uint16_t type_id, int domain, int sock_type, int protocol, PwValuePtr result)
232{
233 PwSocketCtorArgs args = {
234 .type_id = PwTypeId_Socket, // arguments are for this specific type, not for type_id
235 .domain = domain,
236 .type = sock_type,
237 .protocol = protocol
238 };
239 return pw_create2(type_id, &args, result);
240}
241
242[[nodiscard]] static inline bool pw_socket_bind(PwValuePtr sock, PwValuePtr local_addr)
243{
244 return pw_call(Socket, bind, sock, local_addr);
245}
246
247[[nodiscard]] static inline bool pw_socket_reuse_addr(PwValuePtr sock, bool reuse)
248{
249 return pw_call(Socket, reuse_addr, sock, reuse);
250}
251
252[[nodiscard]] static inline bool pw_socket_listen(PwValuePtr sock, int backlog, uint16_t new_sock_type)
253{
254 return pw_call(Socket, listen, sock, backlog, new_sock_type);
255}
256
257[[nodiscard]] static inline bool pw_socket_is_listening(PwValuePtr sock, bool* result)
258{
259 return pw_call(Socket, is_listening, sock, result);
260}
261
262[[nodiscard]] static inline bool pw_socket_accept(PwValuePtr sock, PwValuePtr result)
263{
264 return pw_call(Socket, accept, sock, result);
265}
266
267[[nodiscard]] static inline bool pw_socket_connect(PwValuePtr sock, PwValuePtr remote_addr)
268{
269 return pw_call(Socket, connect, sock, remote_addr);
270}
271
272[[nodiscard]] static inline bool pw_socket_shutdown(PwValuePtr sock, int how)
273{
274 return pw_call(Socket, shutdown, sock, how);
275}
276
277[[nodiscard]] static inline bool pw_get_socket_error(PwValuePtr sock, PwValuePtr result)
278{
279 return pw_call(Socket, get_socket_error, sock, result);
280}
281
282#ifdef __cplusplus
283}
284#endif