The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
base.h
Go to the documentation of this file.
1 #pragma once
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or (at
6  * your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16  */
17 
18 /**
19  * $Id: ea3061098c4704c6f1e9f8770e24fd5309b719ce $
20  * @file lib/redis/base.h
21  * @brief Common functions for interacting with Redis via hiredis
22  *
23  * @author Arran Cudbard-Bell
24  *
25  * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26  * @copyright 2000,2006,2015 The FreeRADIUS server project
27  * @copyright 2011 TekSavvy Solutions (gabe@teksavvy.com)
28  */
29 RCSIDH(redis_h, "$Id: ea3061098c4704c6f1e9f8770e24fd5309b719ce $")
30 
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/server/map.h>
33 #include <freeradius-devel/server/module.h>
34 
35 //DIAG_OFF(extra-semi-stmt)
36 #include <hiredis/hiredis.h>
37 //DIAG_ON(extra-semi-stmt)
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 #define MAX_REDIS_COMMAND_LEN 4096
44 #define MAX_REDIS_ARGS 256
45 
46 #define REDIS_ERROR_MOVED_STR "MOVED"
47 #define REDIS_ERROR_ASK_STR "ASK"
48 #define REDIS_ERROR_TRY_AGAIN_STR "TRYAGAIN"
49 #define REDIS_ERROR_NO_SCRIPT_STR "NOSCRIPT"
50 #define REDIS_DEFAULT_PORT 6379
51 
53 
54 /** Wrap freeReplyObject so we consistently check for NULL pointers
55  *
56  * Older versions such as 0.10 (which ship with Ubuntu <= 14.10)
57  * don't check for NULL pointer before attempting to free, so we
58  * get a NULL pointer dereference in some cases.
59  *
60  * Rather than go back through the many calls to freeReplyObject
61  * and attempt to determine code paths that may result in it being
62  * called on a NULL pointer, we use this to always check.
63  */
64 static inline void fr_redis_reply_free(redisReply **reply)
65 {
66  if (*reply) freeReplyObject(*reply);
67  *reply = NULL;
68 }
69 
70 static inline void fr_redis_pipeline_free(redisReply *reply[], size_t num)
71 {
72  size_t i;
73  for (i = 0; i < num; i++) fr_redis_reply_free(&(reply[i]));
74 }
75 
77 extern size_t redis_reply_types_len;
78 extern fr_table_num_sorted_t const redis_rcodes[];
79 extern size_t redis_rcodes_len;
80 
81 /** Codes are ordered inversely by priority
82  *
83  * To simplify handling the return codes from pipelined commands,
84  * the lowest status code, and the reply which accompanies it should
85  * be returned to the redis cluster code.
86  */
87 typedef enum {
88  REDIS_RCODE_SUCCESS = 0, //!< Operation was successful.
89  REDIS_RCODE_ERROR = -1, //!< Unrecoverable library/server error.
90  REDIS_RCODE_TRY_AGAIN = -2, //!< Try the operation again.
91  REDIS_RCODE_RECONNECT = -3, //!< Transitory error, caller should retry the operation
92  //!< with a new connection.
93  REDIS_RCODE_ASK = -4, //!< Attempt operation on an alternative node.
94  REDIS_RCODE_MOVE = -5, //!< Attempt operation on an alternative node with remap.
95  REDIS_RCODE_NO_SCRIPT = -6, //!< Script doesn't exist.
97 
98 /** Connection handle, holding a redis context
99  */
100 typedef struct {
101  redisContext *handle; //!< Hiredis context used when issuing commands.
102  fr_redis_cluster_node_t *node; //!< Node this connection is to.
104 
105 /** Configuration parameters for a redis connection
106  *
107  * @note should be passed as instance data to #module_rlm_connection_pool_init.
108  */
109 typedef struct {
110  char const **hostname; //!< of Redis server.
111  uint16_t port; //!< of Redis daemon.
112  uint32_t database; //!< number on Redis server.
113  bool use_tls; //!< use TLS.
114  bool use_cluster_map;//!< use cluster map.
115 
116  char const *username; //!< for acls.
117  char const *password; //!< to authenticate to Redis.
118 
119  uint8_t max_nodes; //!< Maximum number of cluster nodes to connect to.
120  uint32_t max_redirects; //!< Maximum number of times we can be redirected.
121  uint32_t max_retries; //!< Maximum number of times we attempt a command
122  //!< when receiving successive -TRYAGAIN messages.
123  uint32_t max_alt; //!< Maximum alternative nodes to try.
124  fr_time_delta_t retry_delay; //!< How long to wait when we received a -TRYAGAIN
125  //!< message.
127 
129 
130  char const *log_prefix;
132 
133 #define REDIS_COMMON_CONFIG \
134  { FR_CONF_OFFSET_FLAGS("server", CONF_FLAG_REQUIRED | CONF_FLAG_MULTI, fr_redis_conf_t, hostname) }, \
135  { FR_CONF_OFFSET("port", fr_redis_conf_t, port), .dflt = "6379" }, \
136  { FR_CONF_OFFSET("database", fr_redis_conf_t, database), .dflt = "0" }, \
137  { FR_CONF_OFFSET("use_tls", fr_redis_conf_t, use_tls), .dflt = "no" }, \
138  { FR_CONF_OFFSET("use_cluster_map", fr_redis_conf_t, use_cluster_map), .dflt = "yes" }, \
139  { FR_CONF_OFFSET("username", fr_redis_conf_t, username) }, \
140  { FR_CONF_OFFSET_FLAGS("password", CONF_FLAG_SECRET, fr_redis_conf_t, password) }, \
141  { FR_CONF_OFFSET("max_nodes", fr_redis_conf_t, max_nodes), .dflt = "20" }, \
142  { FR_CONF_OFFSET("max_alt", fr_redis_conf_t, max_alt), .dflt = "3" }, \
143  { FR_CONF_OFFSET("max_redirects", fr_redis_conf_t, max_redirects), .dflt = "2" }
144 
145 void fr_redis_version_print(void);
146 
147 /*
148  * Command and resulting parsing
149  */
151 
152 void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx);
153 
154 int fr_redis_reply_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, redisReply *reply,
155  fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
156  bool box_error, bool shallow) CC_HINT(nonnull(2,3));
157 
158 int fr_redis_reply_to_map(TALLOC_CTX *ctx, map_list_t *out,
159  request_t *request, redisReply *key, redisReply *op, redisReply *value);
160 
161 int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[], map_t *map);
162 
163 fr_redis_rcode_t fr_redis_get_version(char *out, size_t out_len, fr_redis_conn_t *conn);
164 
165 uint32_t fr_redis_version_num(char const *version);
166 
167 /*
168  * Process response from pipelined command.
169  */
170 fr_redis_rcode_t fr_redis_pipeline_result(unsigned int *pipelined, fr_redis_rcode_t *rcode,
171  redisReply *out[], size_t out_len,
172  fr_redis_conn_t *conn) CC_HINT(nonnull);
173 
174 #ifdef __cplusplus
175 }
176 #endif
#define RCSIDH(h, id)
Definition: build.h:482
A Redis cluster node.
Definition: cluster.c:213
Test enumeration values.
Definition: dict_test.h:92
fr_log_lvl_t
Definition: log.h:67
unsigned short uint16_t
Definition: merged_model.c:31
fr_type_t
Definition: merged_model.c:80
unsigned int uint32_t
Definition: merged_model.c:33
unsigned char uint8_t
Definition: merged_model.c:30
static void fr_redis_pipeline_free(redisReply *reply[], size_t num)
Definition: base.h:70
int fr_redis_reply_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, redisReply *reply, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, bool box_error, bool shallow))
Convert a string or integer type to fr_value_box_t of specified type.
Definition: redis.c:206
uint8_t max_nodes
Maximum number of cluster nodes to connect to.
Definition: base.h:119
fr_time_delta_t reconnection_delay
Definition: base.h:128
fr_table_num_sorted_t const redis_rcodes[]
Definition: redis.c:40
bool use_cluster_map
use cluster map.
Definition: base.h:114
redisContext * handle
Hiredis context used when issuing commands.
Definition: base.h:101
char const * username
for acls.
Definition: base.h:116
uint32_t database
number on Redis server.
Definition: base.h:112
fr_time_delta_t connection_timeout
Definition: base.h:126
bool use_tls
use TLS.
Definition: base.h:113
int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[], map_t *map)
Add a single map pair to an existing command string as three elements.
Definition: redis.c:459
fr_redis_cluster_node_t * node
Node this connection is to.
Definition: base.h:102
uint16_t port
of Redis daemon.
Definition: base.h:111
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
Print the response data in a useful treelike form.
Definition: redis.c:141
size_t redis_reply_types_len
Definition: redis.c:38
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition: base.h:64
uint32_t max_redirects
Maximum number of times we can be redirected.
Definition: base.h:120
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition: redis.c:53
uint32_t fr_redis_version_num(char const *version)
Convert version string into a 32bit unsigned integer for comparisons.
Definition: redis.c:688
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition: redis.c:71
fr_table_num_sorted_t const redis_reply_types[]
Definition: redis.c:30
size_t redis_rcodes_len
Definition: redis.c:48
fr_time_delta_t retry_delay
How long to wait when we received a -TRYAGAIN message.
Definition: base.h:124
char const * log_prefix
Definition: base.h:130
char const ** hostname
of Redis server.
Definition: base.h:110
fr_redis_rcode_t fr_redis_pipeline_result(unsigned int *pipelined, fr_redis_rcode_t *rcode, redisReply *out[], size_t out_len, fr_redis_conn_t *conn)
Simplifies handling of pipelined commands with Redis cluster.
Definition: redis.c:535
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: base.h:87
@ REDIS_RCODE_RECONNECT
Transitory error, caller should retry the operation with a new connection.
Definition: base.h:91
@ REDIS_RCODE_SUCCESS
Operation was successful.
Definition: base.h:88
@ REDIS_RCODE_MOVE
Attempt operation on an alternative node with remap.
Definition: base.h:94
@ REDIS_RCODE_TRY_AGAIN
Try the operation again.
Definition: base.h:90
@ REDIS_RCODE_NO_SCRIPT
Script doesn't exist.
Definition: base.h:95
@ REDIS_RCODE_ASK
Attempt operation on an alternative node.
Definition: base.h:93
@ REDIS_RCODE_ERROR
Unrecoverable library/server error.
Definition: base.h:89
char const * password
to authenticate to Redis.
Definition: base.h:117
fr_redis_rcode_t fr_redis_get_version(char *out, size_t out_len, fr_redis_conn_t *conn)
Get the version of Redis running on the remote server.
Definition: redis.c:638
uint32_t max_alt
Maximum alternative nodes to try.
Definition: base.h:123
uint32_t max_retries
Maximum number of times we attempt a command when receiving successive -TRYAGAIN messages.
Definition: base.h:121
int fr_redis_reply_to_map(TALLOC_CTX *ctx, map_list_t *out, request_t *request, redisReply *key, redisReply *op, redisReply *value)
Convert a pair of redis reply objects to a map.
Definition: redis.c:365
Configuration parameters for a redis connection.
Definition: base.h:109
Connection handle, holding a redis context.
Definition: base.h:100
Value pair map.
Definition: map.h:77
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:49
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
int nonnull(2, 5))
static size_t char ** out
Definition: value.h:997