You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.9 KiB

2 years ago
/**
* Generic implementation of a TCP socket server.
*/
#ifndef _SOCKET_SERVER_H_
#define _SOCKET_SERVER_H_
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifndef ESP_PLATFORM
#define ESP_OK 0 /*!< esp_err_t value indicating success (no error) */
#define ESP_FAIL (-1) /*!< Generic esp_err_t code indicating failure */
#define ESP_ERR_NO_MEM 0x101 /*!< Out of memory */
#define ESP_ERR_INVALID_ARG 0x102 /*!< Invalid argument */
#define ESP_ERR_INVALID_STATE 0x103 /*!< Invalid state */
#define ESP_ERR_INVALID_SIZE 0x104 /*!< Invalid size */
#define ESP_ERR_NOT_FOUND 0x105 /*!< Requested resource not found */
#define ESP_ERR_NOT_SUPPORTED 0x106 /*!< Operation or feature not supported */
#define ESP_ERR_TIMEOUT 0x107 /*!< Operation timed out */
#else
#include <esp_err.h>
#include "sdkconfig.h"
#endif
typedef struct sockd_server *Tcpd_t;
typedef struct sockd_client *TcpdClient_t;
typedef int tcpd_err_t;
#define FD_NONE (-1)
/**
* Socket read handler
*/
typedef tcpd_err_t (*tcpd_read_fn_t)(Tcpd_t serv, TcpdClient_t client, int sockfd);
/**
* Socket open handler
*/
typedef tcpd_err_t (*tcpd_open_fn_t)(Tcpd_t serv, TcpdClient_t client);
/**
* Socket close handler
*/
typedef tcpd_err_t (*tcpd_close_fn_t)(Tcpd_t serv, TcpdClient_t client);
/**
* Function called during server shutdown to free the server context
*/
typedef void (*sockd_sctx_free_fn_t)(void * sctx);
/**
* Server config structure
*/
typedef struct tcpd_config {
uint16_t port; //!< Server port
uint16_t max_clients; //!< Max number of connected clients
bool close_lru; //!< Close the least recently used client when a new connection is received
bool start_immediately; //!< If true, start the server immediately after init, otherwise it starts paused
void *sctx; //!< Server context (arbitrary user data accessible from the callbacks)
sockd_sctx_free_fn_t sctx_free_fn; //!< Context freeing function (no-op if NULL)
tcpd_read_fn_t read_fn; //!< Callback to read data from a socket.
tcpd_open_fn_t open_fn; //!< Callback to init a new client connection. Can set the client tag or handle.
tcpd_close_fn_t close_fn; //!< Callback when a client left or is kicked. Can free the client context.
const char *task_name; //!< Server task name
uint32_t task_stack; //!< Server stack size
uint8_t task_prio; //!< Server priority
} tcpd_config_t;
#define TCPD_INIT_DEFAULT() \
{ \
.port = 23, \
.max_clients = 1, \
.close_lru = true, \
.start_immediately = true, \
\
.sctx = NULL, \
.sctx_free_fn = NULL, \
\
.read_fn = NULL, \
.open_fn = NULL, \
.close_fn = NULL, \
\
.task_name = "socksrv", \
.task_stack = 2048, \
.task_prio = 3, \
}
struct tcpd_client_iter {
Tcpd_t server;
uint16_t next;
};
/** Initializer for the client iterator */
tcpd_err_t tcpd_iter_init(struct tcpd_client_iter *iter, Tcpd_t serv);
/** Iterate active clients. Returns NULL if no more clients were found. */
TcpdClient_t tcpd_client_iter_next(struct tcpd_client_iter *iterator);
/**
* Get server context. The context was defined in the config object.
*
* @param serv - server handle
* @return server context
*/
void *tcpd_get_server_ctx(Tcpd_t serv);
/**
* Get client context, set by socksrv_set_client_ctx()
*
* @param client - client handle
* @return context object
*/
void *tcpd_get_client_ctx(TcpdClient_t client);
/**
* Set client context. If allocated, it should be freed by the client close function.
*
* @param client - client handle
* @param cctx - context object
*/
void tcpd_set_client_ctx(TcpdClient_t client, void *cctx);
/**
* Get client tag.
*
* @param client - client handle
* @return tag value
*/
uint32_t tcpd_get_client_tag(TcpdClient_t client);
/**
* Set client tag. Tag may be used alongside the client context e.g. to distinguish
* context type.
*
* @param client - client handle
* @param tag - tag value
*/
void tcpd_set_client_tag(TcpdClient_t client, uint32_t tag);
/**
* Get client IP address
*
* @param client - client
* @return address struct or NULL
*/
const struct sockaddr_in * tcpd_get_client_addr(TcpdClient_t client);
/**
* Get client FD
*
* @param client
* @return fd
*/
int tcpd_get_client_fd(TcpdClient_t client);
/**
* Kick a single client.
* This may be called even when the server is stopped.
*
* The client handle should be considered invalid after this call,
* as it may be reused for another incoming connection.
* Set it to NULL for safety.
*
* @param client - client handle (obtained e.g. as an argument in the receive function)
*/
void tcpd_kick(TcpdClient_t client);
/**
* Kick all connected clients.
* This may be called even when the server is stopped.
*
* @param serv - server handle
*/
void tcpd_kick_all(Tcpd_t serv, bool with_injected);
/* Kick clients with tag. Returns kicked count, or -1 on err */
int tcpd_kick_by_tag(Tcpd_t serv, uint32_t tag);
/** Kick clients with a given IP. Returns kicked count, or -1 on err */
int tcpd_kick_by_ip(Tcpd_t serv, const struct in_addr *addr);
/**
* Inject a client with a custom FD (e.g. STDIN, other UART socket...).
*
* Injecting STDIN will automatically use STDOUT for outgoing messages.
*
* @param server
* @param fd
* @return the client, NULL on failure
*/
TcpdClient_t tcpd_inject_client(Tcpd_t server, int fd);
/**
* Initialize and start the socket server.
*
* @param config - config struct (will be copied into the server handle, can be only on stack)
* @param handle - pointer where to store the server handle.
* @return success
*/
tcpd_err_t tcpd_init(const tcpd_config_t *config, Tcpd_t *handle);
/**
* Shutdown the server, close open sockets and free all allocated memory.
* Client contexts can be freed in the close_fn, if it was defined in server config.
* It will be called for all still open sockets.
*
* The server context will be freed using the user-provided free function (set in config)
*
* The server handle should be considered invalid after calling this function.
* The same applies to all existing client handles. Set it to NULL for safety.
*
* @param serv - server handle
*/
void tcpd_shutdown(Tcpd_t serv);
/**
* Stop the server loop. It won't accept any connection requests nor data.
* A stopped server can be resumed again.
*
* Does nothing if the server is already stopped.
*
* @param serv - server handle
*/
void tcpd_suspend(Tcpd_t serv);
/**
* Start the server after it has been stopped.
*
* Does nothing if the server is already running.
*
* The server runs immediately after init,
* so this does not need to be called to start it.
*
* @param serv - server handle
*/
void tcpd_resume(Tcpd_t serv);
/**
* Send data to all connected clients
*
* @param serv - server handle
* @param buffer - data to send
* @param len - data length; if negative, treat data as a string and use strlen()
*/
tcpd_err_t tcpd_broadcast(Tcpd_t serv, const uint8_t *data, ssize_t len);
/**
* Send a message to a single client
*
* @param client - client handle
* @param data - bytes to send
* @param len - length or -1 for strlen
* @return success
*/
tcpd_err_t tcpd_send(TcpdClient_t client, const uint8_t *data, ssize_t len);
/**
* Get client slot by FD. Returns NULL if not found.
*
* @param serv - server struct
* @param sockfd
* @return
*/
TcpdClient_t tcpd_client_by_fd(Tcpd_t serv, int sockfd);
/**
* Get client by tag. Returns NULL if not found.
*
* @param serv - server struct
* @param sockfd
* @return
*/
TcpdClient_t tcpd_client_by_tag(Tcpd_t serv, uint32_t tag);
#endif //_SOCKET_SERVER_H_