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