Skip to content

Commit

Permalink
feat: connection_hooks.h/.c
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyTubongbanua committed Jul 29, 2024
1 parent 261f9c8 commit d082fc2
Show file tree
Hide file tree
Showing 5 changed files with 1,051 additions and 365 deletions.
125 changes: 39 additions & 86 deletions packages/atclient/include/atclient/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,37 @@
#include <mbedtls/ssl.h>
#include <stdbool.h>
#include <stddef.h>

#define ATCLIENT_CONSTANTS_HOST_BUFFER_SIZE 128 // the size of the buffer for the host name
#include "atclient/connection_hooks.h"

// represents the type of connection
typedef enum atclient_connection_type {
ATCLIENT_CONNECTION_TYPE_ATDIRECTORY, // uses '\n' to check if it is connected
ATCLIENT_CONNECTION_TYPE_ATSERVER // uses 'noop:0\r\n' to check if it is connected
} atclient_connection_type;

typedef int(atclient_connection_send_hook)(const unsigned char *src, const size_t src_len, unsigned char *recv,
const size_t recv_size, size_t *recv_len);

typedef enum atclient_connection_hook_type {
ATCLIENT_CONNECTION_HOOK_TYPE_NONE = 0,
ATCLIENT_CONNECTION_HOOK_TYPE_PRE_SEND,
ATCLIENT_CONNECTION_HOOK_TYPE_POST_SEND,
ATCLIENT_CONNECTION_HOOK_TYPE_PRE_RECV,
ATCLIENT_CONNECTION_HOOK_TYPE_POST_RECV,
} atclient_connection_hook_type;

typedef struct atclient_connection_hooks {
bool _is_nested_call; // internal variable for preventing infinite recursion (hooks cannot trigger other hooks in
// their nested calls)
atclient_connection_send_hook *pre_send;
atclient_connection_send_hook *post_send;
atclient_connection_send_hook *pre_recv;
atclient_connection_send_hook *post_recv;
bool readonly_src;
} atclient_connection_hooks;

typedef struct atclient_connection {
atclient_connection_type type;
atclient_connection_type type; // set in atclient_connection_init

bool _is_host_initialized: 1;
char *host; // example: "root.atsign.org"

char host[ATCLIENT_CONSTANTS_HOST_BUFFER_SIZE];
int port; // example: 64
bool _is_port_initialized: 1;
uint16_t port; // example: 64

// atclient_connection_connect sets this to true and atclient_connection_disconnect sets this to false
// this does not mean that the connection is still alive, it just means that the connection was established at least
// once, at some point, check atclient_connection_is_connected for a live status on the connection
// _should_be_connected also serves as an internal boolean to check if the following mbedlts contexts have been
// _is_connection_enabled also serves as an internal boolean to check if the following mbedlts contexts have been
// initialized and need to be freed at the end
bool _should_be_connected;
bool _is_connection_enabled: 1;
mbedtls_net_context net;
mbedtls_ssl_context ssl;
mbedtls_ssl_config ssl_config;
mbedtls_x509_crt cacert;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;

bool _is_hooks_enabled;
bool _is_hooks_enabled: 1;
atclient_connection_hooks *hooks;
} atclient_connection;

Expand All @@ -70,6 +51,13 @@ typedef struct atclient_connection {
*/
void atclient_connection_init(atclient_connection *ctx, atclient_connection_type type);

/**
* @brief free memory allocated by the init function
*
* @param ctx the struct which was previously initialized
*/
void atclient_connection_free(atclient_connection *ctx);

/**
* @brief after initializing a connection context, connect to a host and port
*
Expand All @@ -78,7 +66,28 @@ void atclient_connection_init(atclient_connection *ctx, atclient_connection_type
* @param port the port to connect to
* @return int 0 on success, otherwise error
*/
int atclient_connection_connect(atclient_connection *ctx, const char *host, const int port);
int atclient_connection_connect(atclient_connection *ctx, const char *host, const uint16_t port);

/**
* @brief Reads data from the connection
*
* @param ctx the connection initialized and connected using atclient_connection_init and atclient_connection_connect
* @param value a double pointer that will be allocated by the function to the data read
* @param value_len the length of the data read, will be set by the function
* @param value_max_len the maximum length of the data to read, setting this to 0 means no limit
* @return int 0 on success
*/
int atclient_connection_read(atclient_connection *ctx, unsigned char **value, size_t *value_len, const size_t value_max_len);

/**
* @brief Write data to the connection
*
* @param ctx connection initialized and connected using atclient_connection_init and atclient_connection_connect
* @param value the data to write
* @param value_len the length of the data to write
* @return int 0 on success
*/
int atclient_connection_write(atclient_connection *ctx, const unsigned char *value, const size_t value_len);

/**
* @brief send data to the connection
Expand Down Expand Up @@ -112,60 +121,4 @@ int atclient_connection_disconnect(atclient_connection *ctx);
*/
bool atclient_connection_is_connected(atclient_connection *ctx);

/**
* @brief free memory allocated by the init function
*
* @param ctx the struct which was previously initialized
*/
void atclient_connection_free(atclient_connection *ctx);

/**
* @brief Initialize the hooks memory allocation
*
* @param ctx the struct for the connection
*/
void atclient_connection_enable_hooks(atclient_connection *ctx);

/**
* @brief Add a hook to be called during the connection lifecycle
*
* @param ctx the struct for the connection
* @param type the hook type you want to add
* @param hook the hook function itself
*
* @return int 0 on success, otherwise error
*/
int atclient_connection_hooks_set(atclient_connection *ctx, atclient_connection_hook_type type, void *hook);

/**
* @brief Set whether the readonly_src status for all hooks
*
* @param ctx the struct for the connection
* @param readonly_src the new state for readonly_src
*
* @note For performance, keep readonly_src set to true if you don't need to write access to src
*/
void atclient_connection_hooks_set_readonly_src(atclient_connection *ctx, bool readonly_src);

/**
* @brief Write data to the connection
*
* @param ctx connection initialized and connected using atclient_connection_init and atclient_connection_connect
* @param value the data to write
* @param value_len the length of the data to write
* @return int 0 on success
*/
int atclient_connection_write(atclient_connection *ctx, const unsigned char *value, const size_t value_len);

/**
* @brief Reads data from the connection
*
* @param ctx the connection initialized and connected using atclient_connection_init and atclient_connection_connect
* @param value a double pointer that will be allocated by the function to the data read
* @param value_len the length of the data read, will be set by the function
* @param value_max_len the maximum length of the data to read, setting this to 0 means no limit
* @return int 0 on success
*/
int atclient_connection_read(atclient_connection *ctx, unsigned char **value, size_t *value_len, const size_t value_max_len);

#endif
64 changes: 64 additions & 0 deletions packages/atclient/include/atclient/connection_hooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef ATCLIENT_CONNECTION_HOOKS_H
#define ATCLIENT_CONNECTION_HOOKS_H

#include <atclient/connection.h>
#include <stddef.h>
#include <stdint.h>

#define VALUE_INITIALIZED 0b00000001

#define ATCLIENT_CONNECTION_HOOKS_PRE_READ_INDEX 0
#define ATCLIENT_CONNECTION_HOOKS_POST_READ_INDEX 0
#define ATCLIENT_CONNECTION_HOOKS_PRE_WRITE_INDEX 0
#define ATCLIENT_CONNECTION_HOOKS_POST_WRITE_INDEX 0

#define ATCLIENT_CONNECTION_HOOKS_PRE_READ_INITIALIZED (VALUE_INITIALIZED << 0)
#define ATCLIENT_CONNECTION_HOOKS_POST_READ_INITIALIZED (VALUE_INITIALIZED << 1)
#define ATCLIENT_CONNECTION_HOOKS_PRE_WRITE_INITIALIZED (VALUE_INITIALIZED << 2)
#define ATCLIENT_CONNECTION_HOOKS_POST_WRITE_INITIALIZED (VALUE_INITIALIZED << 3)

typedef struct atclient_connection atclient_connection;

typedef struct atclient_connection_hook_params {
unsigned char *src;
size_t src_len;
unsigned char *recv;
size_t recv_size;
size_t *recv_len;
} atclient_connection_hook_params;

typedef int(atclient_connection_hook)(atclient_connection_hook_params *params);

typedef enum atclient_connection_hook_type {
ATCLIENT_CONNECTION_HOOK_TYPE_NONE = 0,
ATCLIENT_CONNECTION_HOOK_TYPE_PRE_READ,
ATCLIENT_CONNECTION_HOOK_TYPE_POST_READ,
ATCLIENT_CONNECTION_HOOK_TYPE_PRE_WRITE,
ATCLIENT_CONNECTION_HOOK_TYPE_POST_WRITE,
} atclient_connection_hook_type;

typedef struct atclient_connection_hooks {
bool _is_nested_call;
bool readonly_src;
atclient_connection_hook *pre_read;
atclient_connection_hook *post_read;
atclient_connection_hook *pre_write;
atclient_connection_hook *post_write;
uint8_t _initialized_fields[1];
} atclient_connection_hooks;

bool atclient_connection_hooks_is_enabled(atclient_connection *ctx);
int atclient_connection_hooks_enable(atclient_connection *conn);
void atclient_connection_hooks_disable(atclient_connection *conn);

// Q. Why is hook a void pointer?
// A. In case we want to add future hook types which use a different function signature
int atclient_connection_hooks_set(atclient_connection *ctx, const atclient_connection_hook_type type, void *hook);

bool atclient_connection_hooks_is_pre_read_initialized(const atclient_connection *ctx);
bool atclient_connection_hooks_is_post_read_initialized(const atclient_connection *ctx);
bool atclient_connection_hooks_is_pre_write_initialized(const atclient_connection *ctx);
bool atclient_connection_hooks_is_post_write_initialized(const atclient_connection *ctx);


#endif
Loading

0 comments on commit d082fc2

Please sign in to comment.