28#include <freeradius-devel/redis/io.h>
29#include <freeradius-devel/util/debug.h>
32#include <hiredis/hiredis_ssl.h>
55 switch (conn->state) {
75 DEBUG4(
"Signalled by hiredis, connection disconnected");
86 redisReply *reply =
data;
89 ERROR(
"Failed selecting database: %s", ac->errstr);
95 switch (reply->type) {
96 case REDIS_REPLY_STATUS:
97 if (strcmp(reply->str,
"OK") != 0) {
98 ERROR(
"Failed selecting database: %s", reply->str);
103 case REDIS_REPLY_ERROR:
104 ERROR(
"Failed selecting database: %s", reply->str);
108 ERROR(
"Unexpected reply of type %s to SELECT",
122 redisReply *reply =
data;
127 ERROR(
"Failed authenticating: %s", ac->errstr);
133 switch (reply->type) {
134 case REDIS_REPLY_STATUS:
135 if (strcmp(reply->str,
"OK") != 0) {
136 ERROR(
"Failed authenticating: %s", reply->str);
141 case REDIS_REPLY_ERROR:
142 ERROR(
"Failed authenticating: %s", reply->str);
146 ERROR(
"Unexpected reply of type %s to AUTH",
152 DEBUG3(
"Executing: SELECT %d", database);
153 if (redisAsyncCommand(ac,
_redis_select_result, NULL,
"SELECT %d", database) != REDIS_OK)
goto error;
169 DEBUG4(
"Signalled by hiredis, connection is open");
173 fr_tls_session_t *tls_session = fr_tls_session_alloc_client(conn, conn_uctx->ssl_ctx);
176 fr_tls_strerror_printf(
"%s - [%s]", io_conf->
log_prefix, conn->name);
183 SSL_up_ref(tls_session->ssl);
184 if (redisInitiateSSL(&ac->c, tls_session->ssl) != REDIS_OK) {
185 ERROR(
"%s - Failed to initiate SSL: %s", io_conf->
log_prefix, ac->c.errstr);
186 SSL_free(tls_session->ssl);
203 ERROR(
"Failed executing command");
222#ifndef HAVE_REDIS_CALLBACKNC
229 redisAsyncContext *our_ac =
UNCONST(redisAsyncContext *, ac);
243 DEBUG4(
"redis handle %p - FD %i now readable", h, fd);
245 redisAsyncHandleRead(h->
ac);
256 DEBUG4(
"redis handle %p - FD %i now writable", h, fd);
258 redisAsyncHandleWrite(h->
ac);
267 int fd_errno,
void *uctx)
272 ERROR(
"%s handle %p - FD %i errored: %s", conn->name, h, fd,
fr_syserror(fd_errno));
290 redisContext *c = &(h->
ac->c);
299 if (!read && !write) {
300 DEBUG4(
"redis handle %p - De-registering FD %i", h, c->fd);
303 PERROR(
"redis handle %p - De-registration failed for FD %i", h, c->fd);
309 DEBUG4(
"redis handle %p - Registered for %s%serror events on FD %i",
310 h, read ?
"read+" :
"", write ?
"write+" :
"", c->fd);
317 PERROR(
"redis handle %p - Registration failed for %s%serror events on FD %i",
318 h, read ?
"read+" :
"", write ?
"write+" :
"", c->fd);
370#ifdef HAVE_REDIS_TIMEOUT
379 DEBUG4(
"redis handle %p - Timeout", h);
381 redisAsyncHandleTimeout(h->
ac);
387static void _redis_io_timer_modify(
void *uctx,
struct timeval tv)
398 false, _redis_io_service_timer_expired, conn) < 0) {
399 PERROR(
"redis timeout %p - Failed adding timeout", h);
433 DEBUG4(
"redis handle %p - Freed", h);
444 if (ac->ev.data != NULL)
return REDIS_ERR;
450#ifdef HAVE_REDIS_TIMEOUT
451 ac->ev.scheduleTimer = _redis_io_timer_modify;
454 memcpy(&ac->ev.data, &conn,
sizeof(ac->ev.data));
469 if (h->
ac) redisAsyncFree(h->
ac);
502 h->
ac = redisAsyncConnect(host, port);
504 ERROR(
"Failed allocating handle for %s:%u", host, port);
509 ERROR(
"Failed allocating handle for %s:%u: %s", host, port, h->
ac->errstr);
511 redisAsyncFree(h->
ac);
523 memcpy(&h->
ac->data, &conn,
sizeof(h->
ac->data));
550#ifdef HAVE_REDIS_CALLBACKNC
555 if (ret != REDIS_OK) {
556 ERROR(
"Failed setting connected callback: Error %i", ret);
561 if (ret != REDIS_OK) {
562 ERROR(
"Failed setting disconnected callback: Error %i", ret);
578 redisAsyncDisconnect(our_h->
ac);
615 char const *log_prefix)
623 uctx->ssl_ctx = ssl_ctx;
642 if (!conn)
return NULL;
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define CC_NO_UBSAN(_sanitize)
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_CLOSED
Connection has been closed.
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
@ CONNECTION_STATE_SHUTDOWN
Connection is shutting down.
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Holds a complete set of functions for a connection.
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
#define fr_event_fd_insert(...)
static void _redis_io_service_errored(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, int fd_errno, void *uctx)
Redis FD errored - Automatically removes registered events.
static void _redis_io_del_write(void *uctx)
De-register FD for writes.
redisAsyncContext * fr_redis_connection_get_async_ctx(connection_t *conn)
Return the redisAsyncContext associated with the connection.
static void _redis_io_del_read(void *uctx)
De-register FD for reads.
static int _redis_handle_free(fr_redis_handle_t *h)
Free the redis async context when the handle is freed.
static void _redis_io_add_write(void *uctx)
Register FD for writes.
static connection_state_t _redis_io_connection_shutdown(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
Gracefully signal that the connection should shutdown.
static void _redis_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Redis FD became writable.
static void _redis_select_result(struct redisAsyncContext *ac, void *data, UNUSED void *privdata)
Callback for verifying the results of SELECT.
static connection_state_t _redis_io_connection_init(void **h_out, connection_t *conn, void *uctx)
Callback for the initialise state.
static void _redis_io_free(void *uctx)
Handle freeing the redisAsyncContext.
fr_redis_io_conf_t const * io_conf
static void _redis_connected_nc(redisAsyncContext *ac, UNUSED int status)
Called by hiredis to indicate the connection is live.
static void _redis_auth_result(struct redisAsyncContext *ac, void *data, UNUSED void *privdata)
Callback for verifying the results of AUTH.
static void _redis_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Redis FD became readable.
static void _redis_connected(redisAsyncContext const *ac, int status)
connection_t * fr_redis_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_conf_t const *conn_conf, fr_redis_io_conf_t const *io_conf, char const *log_prefix)
Allocate an async redis I/O connection.
static void _redis_io_add_read(void *uctx)
Register FD for reads.
static int fr_redis_io_setup(redisAsyncContext *ac, connection_t const *conn)
Configures async I/O callbacks for an existing redisAsyncContext.
static void _redis_disconnected(redisAsyncContext const *ac, UNUSED int status)
Called by hiredis to indicate the connection is dead.
static void _redis_io_common(connection_t *conn, fr_redis_handle_t *h, bool read, bool write)
Deal with the method hiredis uses to register/unregister interest in a file descriptor.
static void _redis_io_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
Notification that the connection has errored and must be closed.
bool read_set
We're listening for reads.
redisAsyncContext * ac
Async handle for hiredis.
fr_dlist_head_t ignore
Contains SQNs for responses that should be ignored.
fr_event_fd_t * fd_ev
IO event.
char const * username
to authenticate to Redis.
fr_timer_t * timer_ev
Connection timer.
uint32_t database
number on Redis server.
char const * password
to authenticate to Redis.
bool write_set
We're listening for writes.
bool freeing
Ensure that redisAsyncFree doesn't cause a callback loop.
int fr_event_fd_delete_handle(fr_event_fd_t *ef)
Remove a file descriptor from the event loop, by handle.
Stores all information relating to an event list.
fr_table_num_sorted_t const redis_reply_types[]
void * connection_uctx_get(connection_t *conn)
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
#define talloc_get_type_abort_const
static fr_time_delta_t fr_time_delta_from_timeval(struct timeval const *tv)
A time delta, a difference in time measured in nanoseconds.
static fr_event_list_t * el
#define fr_box_time_delta(_val)