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