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