The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
redis.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 33c5b1728ce1bd90211e8bbfb691d185ed35b50e $
19 * @file redis.c
20 * @brief Common functions for interacting with Redis via hiredis
21 *
22 * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @copyright 2000,2006,2015 The FreeRADIUS server project
24 * @copyright 2011 TekSavvy Solutions (gabe@teksavvy.com)
25 */
26#include <freeradius-devel/redis/base.h>
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/util/value.h>
29
31 { L("array"), REDIS_REPLY_ARRAY },
32 { L("error"), REDIS_REPLY_ERROR },
33 { L("integer"), REDIS_REPLY_INTEGER },
34 { L("nil"), REDIS_REPLY_NIL },
35 { L("status"), REDIS_REPLY_STATUS },
36 { L("string"), REDIS_REPLY_STRING }
37};
39
41 { L("ask"), REDIS_RCODE_ASK },
42 { L("error"), REDIS_RCODE_ERROR },
43 { L("move"), REDIS_RCODE_MOVE },
44 { L("reconnect"), REDIS_RCODE_RECONNECT },
45 { L("success"), REDIS_RCODE_SUCCESS },
46 { L("try again"), REDIS_RCODE_TRY_AGAIN }
47};
49
50/** Print the version of libhiredis the server was built against
51 *
52 */
54{
55 INFO("libfreeradius-redis: libhiredis version: %i.%i.%i", HIREDIS_MAJOR, HIREDIS_MINOR, HIREDIS_PATCH);
56}
57
58/** Check the reply for errors
59 *
60 * @param[in] conn used to issue the command.
61 * @param[in] reply to process.
62 * @return
63 * - REDIS_RCODE_TRY_AGAIN - If the operation should be retries.
64 * - REDIS_RCODE_MOVED - If the key has been permanently moved.
65 * - REDIS_RCODE_ASK - If the key has been temporarily moved.
66 * - REDIS_RCODE_SUCCESS - if no errors.
67 * - REDIS_RCODE_ERROR - on command/server error.
68 * - REDIS_RCODE_NO_SCRIPT - script specified by evalsha doesn't exist.
69 * - REDIS_RCODE_RECONNECT - on connection error (probably needs reconnecting).
70 */
72{
73 size_t i = 0;
74
75 if (!reply) switch (conn->handle->err) {
76 case REDIS_OK:
77 break;
78
79 case REDIS_ERR_IO:
80 case REDIS_ERR_EOF:
81 case REDIS_ERR_OTHER:
82 fr_strerror_printf("Connection error: %s", conn->handle->errstr);
84
85 default:
86 case REDIS_ERR_PROTOCOL:
87 fr_strerror_printf("Command error: %s", conn->handle->errstr);
88 return REDIS_RCODE_ERROR;
89 }
90
91 if (reply) switch (reply->type) {
92 case REDIS_REPLY_STATUS:
94
95 case REDIS_REPLY_ERROR:
96 /*
97 * May indicate the connection handle is bad.
98 */
99 fr_assert_msg(reply->str, "Error response contained no error string");
100
101 fr_strerror_printf("Server error: %s", reply->str);
102 if (strncmp(REDIS_ERROR_MOVED_STR, reply->str, sizeof(REDIS_ERROR_MOVED_STR) - 1) == 0) {
103 return REDIS_RCODE_MOVE;
104 }
105 if (strncmp(REDIS_ERROR_ASK_STR, reply->str, sizeof(REDIS_ERROR_ASK_STR) - 1) == 0) {
106 return REDIS_RCODE_ASK;
107 }
108 if (strncmp(REDIS_ERROR_TRY_AGAIN_STR, reply->str, sizeof(REDIS_ERROR_TRY_AGAIN_STR) - 1) == 0) {
110 }
111 if (strncmp(REDIS_ERROR_NO_SCRIPT_STR, reply->str, sizeof(REDIS_ERROR_NO_SCRIPT_STR) - 1) == 0) {
113 }
114 return REDIS_RCODE_ERROR;
115
116 /*
117 * Recurse to check for nested errors
118 */
119 case REDIS_REPLY_ARRAY:
120 for (i = 0; i < reply->elements; i++) {
121 int ret;
122
123 ret = fr_redis_command_status(conn, reply->element[i]);
124 if (ret < 0) return ret;
125 }
126 break;
127
128 default:
129 break;
130 }
131 return REDIS_RCODE_SUCCESS;
132}
133
134/** Print the response data in a useful treelike form
135 *
136 * @param[in] lvl to print data at.
137 * @param[in] reply to print.
138 * @param[in] request The current request.
139 * @param[in] idx Response number.
140 */
141void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
142{
143 size_t i = 0;
144
145 if (!reply) return;
146
147 switch (reply->type) {
148 case REDIS_REPLY_ERROR:
149 ROPTIONAL(REDEBUG, ERROR, "(%i) error : %s", idx, reply->str);
150 break;
151
152 case REDIS_REPLY_STATUS:
153 ROPTIONAL(RDEBUGX, DEBUGX, lvl, "(%i) status : %s", idx, reply->str);
154 break;
155
156 case REDIS_REPLY_STRING:
157 ROPTIONAL(RDEBUGX, DEBUGX, lvl, "(%i) string : %s", idx, reply->str);
158 break;
159
160 case REDIS_REPLY_INTEGER:
161 ROPTIONAL(RDEBUGX, DEBUGX, lvl, "(%i) integer : %lld", idx, reply->integer);
162 break;
163
164 case REDIS_REPLY_NIL:
165 ROPTIONAL(RDEBUGX, DEBUGX, lvl, "(%i) nil", idx);
166 break;
167
168 case REDIS_REPLY_ARRAY:
169 ROPTIONAL(RDEBUGX, DEBUGX, lvl, "(%i) array[%zu]", idx, reply->elements);
170 for (i = 0; i < reply->elements; i++) {
171 if (request) RINDENT();
172 fr_redis_reply_print(lvl, reply->element[i], request, i);
173 if (request) REXDENT();
174 }
175 break;
176 }
177}
178
179/** Convert a string or integer type to #fr_value_box_t of specified type
180 *
181 * Will work with REDIS_REPLY_STRING (which is converted to #FR_TYPE_STRING
182 * then cast to dst_type), or REDIS_REPLY_INTEGER (which is converted to
183 * #FR_TYPE_UINT64, then cast to dst_type).
184 *
185 * @note Any unsupported types will trigger an assert. You must check the
186 * reply type prior to calling this function.
187 *
188 * @param[in,out] ctx to allocate any buffers in.
189 * @param[out] out Where to write the cast type.
190 * @param[in] reply to process.
191 * @param[in] dst_type to convert to. May be FR_TYPE_VOID
192 * to infer type.
193 * @param[in] dst_enumv Used to convert string types to
194 * integers for attribute with enumerated
195 * values.
196 * @param[in] box_error If true then REDIS_REPLY_ERROR will be
197 * copied to a box, otherwise we'll return
198 * and error with the contents of the error
199 * available on the thread local error stack.
200 * @param[in] shallow If true, we shallow copy strings.
201 * @return
202 * - 1 if we received a NIL reply.
203 * - 0 on success.
204 * - -1 on cast or parse failure.
205 */
206int fr_redis_reply_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, redisReply *reply,
207 fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
208 bool box_error, bool shallow)
209{
211 fr_value_box_t *to_cast;
212
213 if (dst_type != FR_TYPE_VOID) {
215 to_cast = &in;
216 } else {
217 to_cast = out;
218 }
219
220 switch (reply->type) {
221 case REDIS_REPLY_NIL:
222 fr_value_box_init(out, FR_TYPE_NULL, NULL, false);
223 return 1;
224
225 /*
226 * Try and convert the integer to the smallest
227 * and simplest type possible, to give the cast
228 * the greatest chance of success.
229 */
230 case REDIS_REPLY_INTEGER:
231 if (reply->integer < INT32_MIN) { /* 64bit signed */
232 fr_value_box(to_cast, (int64_t) reply->integer, true);
233 }
234 else if (reply->integer < INT16_MIN) { /* 32bit signed */
235 fr_value_box(to_cast, (int32_t) reply->integer, true);
236 }
237 else if (reply->integer < INT8_MIN) { /* 16bit signed */
238 fr_value_box(to_cast, (int16_t) reply->integer, true);
239 }
240 else if (reply->integer < 0) { /* 8bit signed */
241 fr_value_box(to_cast, (int8_t) reply->integer, true);
242 }
243 else if (reply->integer > UINT32_MAX) { /* 64bit unsigned */
244 fr_value_box(to_cast, (uint64_t) reply->integer, true);
245 }
246 else if (reply->integer > UINT16_MAX) { /* 32bit unsigned */
247 fr_value_box(to_cast, (uint32_t) reply->integer, true);
248 }
249 else if (reply->integer > UINT8_MAX) { /* 16bit unsigned */
250 fr_value_box(to_cast, (uint16_t) reply->integer, true);
251 }
252 else { /* 8bit unsigned */
253 fr_value_box(to_cast, (uint8_t) reply->integer, true);
254 }
255 break;
256
257#if HIREDIS_MAJOR >= 1
258 case REDIS_REPLY_DOUBLE:
259 /* reply->str is \0 terminated in this case */
260 fr_value_box(to_cast, strtod(reply->str, NULL), true);
261 break;
262
263 case REDIS_REPLY_BOOL:
264 fr_value_box(to_cast, (bool)reply->integer, true);
265 break;
266#endif
267
268 case REDIS_REPLY_ERROR:
269 if (!box_error) {
270 fr_strerror_printf("Redis error: %pV",
271 fr_box_strvalue_len(reply->str, reply->len));
272 return -1;
273 }
275
276#if HIREDIS_MAJOR >= 1
277 case REDIS_REPLY_BIGNUM: /* FIXME - Could try and convert to integer ? */
278#endif
279 case REDIS_REPLY_STRING:
280 case REDIS_REPLY_STATUS:
281 fr_value_box_init(out, FR_TYPE_STRING, NULL, false);
282
283 if (shallow) {
284 fr_value_box_bstrndup_shallow(to_cast, NULL, reply->str, reply->len, true);
285 } else {
286 if (fr_value_box_bstrndup(to_cast->talloced ? to_cast : ctx, to_cast, NULL,
287 reply->str, reply->len, true) < 0) return -1;
288 }
289 break;
290
291#if HIREDIS_MAJOR >= 1
292 case REDIS_REPLY_VERB:
293 {
294 fr_value_box_t *verb, *vtype;
295
296 fr_value_box_init(out, FR_TYPE_GROUP, NULL, true);
297
298 verb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL);
299 if (unlikely(!verb)) {
300 fr_strerror_const("Out of memory");
301 return -1;
302 }
303 if (fr_value_box_bstrndup(verb, verb, NULL, reply->str, reply->len, true) < 0) return -1;
304 fr_value_box_list_insert_head(&out->vb_group, verb);
305
306 vtype = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL);
307 if (unlikely(!vtype)) {
308 fr_strerror_const("Out of memory");
309 talloc_free(verb);
310 return -1;
311 }
312 if (fr_value_box_strdup(ctx, vtype, NULL, reply->vtype, true) < 0) return -1;
313 fr_value_box_list_insert_head(&out->vb_group, vtype);
314
315 }
316 break;
317#endif
318
319#if HIREDIS_MAJOR >= 1
320 case REDIS_REPLY_SET:
321 case REDIS_REPLY_MAP:
322 case REDIS_REPLY_PUSH:
323#endif
324 case REDIS_REPLY_ARRAY:
325 {
326 fr_value_box_t *vb;
327 size_t i;
328
329 fr_value_box_init(out, FR_TYPE_GROUP, NULL, true);
330
331 for (i = 0; i < reply->elements; i++) {
332 vb = fr_value_box_alloc_null(ctx);
333 if (unlikely(!vb)) {
334 array_error:
335 fr_value_box_list_talloc_free(&out->vb_group);
336 return -1;
337 }
338 fr_value_box_list_insert_tail(&out->vb_group, vb);
339
340 if (fr_redis_reply_to_value_box(vb, vb, reply->element[i],
341 FR_TYPE_VOID, NULL, box_error, shallow) < 0) goto array_error;
342 }
343 }
344 }
345
346 if ((dst_type != FR_TYPE_VOID) && (fr_value_box_cast(ctx, out, dst_type, dst_enumv, to_cast) < 0)) return -1;
347
348 return 0;
349}
350
351/** Convert a pair of redis reply objects to a map
352 *
353 * The maps can then be applied using #map_to_request.
354 *
355 * @param[in,out] ctx to allocate maps in.
356 * @param[out] out Where to write the head of the new maps list.
357 * @param[in] request The current request.
358 * @param[in] key to process.
359 * @param[in] op to process.
360 * @param[in] value to process.
361 * @return
362 * - 0 on success.
363 * - -1 on failure.
364 */
365int fr_redis_reply_to_map(TALLOC_CTX *ctx, map_list_t *out, request_t *request,
366 redisReply *key, redisReply *op, redisReply *value)
367{
368 map_t *map = NULL;
369 ssize_t slen;
370
371 if (key->type != REDIS_REPLY_STRING) {
372 REDEBUG("Bad key type, expected string, got %s",
373 fr_table_str_by_value(redis_reply_types, key->type, "<UNKNOWN>"));
374 error:
375 TALLOC_FREE(map);
376 return -1;
377 }
378
379 if (op->type != REDIS_REPLY_STRING) {
380 REDEBUG("Bad key type, expected string, got %s",
381 fr_table_str_by_value(redis_reply_types, op->type, "<UNKNOWN>"));
382 goto error;
383 }
384
385 RDEBUG3("Got key : %s", key->str);
386 RDEBUG3("Got op : %s", op->str);
387 RDEBUG3("Got value : %pV", fr_box_strvalue_len(value->str, value->len));
388
389 MEM(map = talloc_zero(ctx, map_t));
390 slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, key->str,
391 &(tmpl_rules_t){
392 .attr = {
393 .prefix = TMPL_ATTR_REF_PREFIX_NO,
394 .dict_def = request->dict,
395 .list_def = request_attr_request
396 }
397 });
398 if (slen <= 0) {
399 REMARKER(key->str, -slen, "%s", fr_strerror());
400 goto error;
401 }
402
404 if (map->op == T_INVALID) {
405 REDEBUG("Invalid operator \"%s\"", op->str);
406 goto error;
407 }
408
409 switch (value->type) {
410 case REDIS_REPLY_STRING:
411 case REDIS_REPLY_INTEGER:
412 {
414
415 /* Logs own errors */
417 tmpl_attr_tail_da(map->lhs)->type, tmpl_attr_tail_da(map->lhs), false, false) < 0) {
418 RPEDEBUG("Failed converting Redis data");
419 goto error;
420 }
421
422 /* This will only fail only memory allocation errors */
423 if (tmpl_afrom_value_box(map, &map->rhs, &vb, true) < 0) goto error;
424 }
425 break;
426
427 default:
428 REDEBUG("Bad value type, expected string or integer, got %s",
429 fr_table_str_by_value(redis_reply_types, value->type, "<UNKNOWN>"));
430 goto error;
431
432 }
433 MAP_VERIFY(map);
434
435 map_list_insert_tail(out, map);
436
437 return 0;
438}
439
440/** Add a single map pair to an existing command string as three elements
441 *
442 * - Integer types will be encoded as integers.
443 * - Strings and octets will be encoded in their raw form.
444 * - Other types will be converted to their printable form and will be encoded as strings.
445 *
446 * @note lhs must be a #TMPL_TYPE_ATTR.
447 * @note rhs must be a #TMPL_TYPE_DATA.
448 *
449 * @param pool to allocate any buffers in.
450 * @param out Where to write pointers to the member of the tuple. Unused elements should be
451 * a multiple of three, and it should have at least three unused elements.
452 * @param out_len Where to write the size of the data pointed to by the equivalent index
453 * in the out array.
454 * @param map to convert.
455 * @return
456 * 0 on success.
457 * -1 on failure.
458 */
459int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[], map_t *map)
460{
461 char *new;
462
463 char key_buf[256];
464 fr_sbuff_t key_buf_sbuff = FR_SBUFF_OUT(key_buf, sizeof(key_buf));
465 char *key;
466 size_t key_len;
467 ssize_t slen;
468
471
472 slen = tmpl_print(&key_buf_sbuff, map->lhs, TMPL_ATTR_REF_PREFIX_NO, NULL);
473 if (slen < 0) {
474 fr_strerror_printf("Key too long. Must be < " STRINGIFY(sizeof(key_buf)) " "
475 "bytes, got %zu bytes", (size_t)(slen * -1));
476 return -1;
477 }
478 key_len = (size_t)slen;
479 key = talloc_bstrndup(pool, fr_sbuff_start(&key_buf_sbuff), key_len);
480 if (!key) return -1;
481
482 switch (tmpl_value_type(map->rhs)) {
483 case FR_TYPE_STRING:
484 case FR_TYPE_OCTETS:
485 out[2] = tmpl_value(map->rhs)->datum.ptr;
486 out_len[2] = tmpl_value_length(map->rhs);
487 break;
488
489 /*
490 * For everything else we get the string representation
491 */
492 default:
493 fr_value_box_aprint(pool, &new, tmpl_value(map->rhs), NULL);
494 if (!new) {
495 talloc_free(key);
496 return -1;
497 }
498 out[2] = new;
499 out_len[2] = talloc_array_length(new) - 1;
500 break;
501 }
502
503 out[0] = key;
504 out_len[0] = key_len;
506 out_len[1] = strlen(out[1]);
507
508 return 0;
509}
510
511/** Simplifies handling of pipelined commands with Redis cluster
512 *
513 * Retrieve all available pipelined responses, and write them to the array.
514 *
515 * On encountering an error, all previously retrieved responses are freed, and the reply
516 * containing the error is written to the first element of out. All responses after the
517 * error are also freed.
518 *
519 * If the number of responses != pipelined, that's also an error, a very serious one,
520 * in libhiredis or Redis. We can't really do much here apart from error out.
521 *
522 * @param[out] pipelined Number of pipelined commands we sent to the server.
523 * @param[out] rcode Status of the first errored response, or REDIS_RCODE_SUCCESS
524 * if all responses were processed.
525 * @param[out] out Where to write the replies from pipelined commands.
526 * Will contain exactly 1 element on error WHICH MUST BE FREED,
527 * else the number passed in pipelined.
528 * @param[in] out_len number of elements in out.
529 * @param[in] conn the pipelined commands were issued on.
530 * @return
531 * - #REDIS_RCODE_SUCCESS on success.
532 * - #REDIS_RCODE_ERROR on command/response mismatch or command error.
533 * - REDIS_RCODE_* on other errors;
534 */
536 redisReply *out[], size_t out_len,
537 fr_redis_conn_t *conn)
538{
539 size_t i;
540 redisReply **out_p = out;
542 redisReply *reply = NULL;
543
544 fr_assert(out_len >= (size_t)*pipelined);
545
546 fr_strerror_clear(); /* Clear any outstanding errors */
547
548 if ((size_t) *pipelined > out_len) {
549 for (i = 0; i < (size_t)*pipelined; i++) {
550 if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) break;
551 fr_redis_reply_free(&reply);
552 }
553
554 *pipelined = 0; /* all outstanding responses should be cleared */
555
556 fr_strerror_const("Too many pipelined commands");
557 out[0] = NULL;
558 return REDIS_RCODE_ERROR;
559 }
560
561 for (i = 0; i < (size_t)*pipelined; i++) {
562 bool maybe_more = false;
563
564 /*
565 * we don't need to check the return code here,
566 * as it's also stored in the conn->handle.
567 */
568 reply = NULL; /* redisGetReply doesn't NULLify reply on error *sigh* */
569 if (redisGetReply(conn->handle, (void **)&reply) == REDIS_OK) maybe_more = true;
570 status = fr_redis_command_status(conn, reply);
571 *out_p++ = reply;
572
573 /*
574 * Bail out of processing responses,
575 * free the remaining ones (leaving this one intact)
576 * pass control back to the cluster code.
577 */
578 if (maybe_more && (status != REDIS_RCODE_SUCCESS)) {
579 size_t j;
580 error:
581 /*
582 * Append the hiredis error
583 */
584 if (conn->handle->errstr[0]) fr_strerror_printf_push("%s", conn->handle->errstr);
585
586 /*
587 * Free everything that came before the bad reply
588 */
589 for (j = 0; j < i; j++) {
591 out[j] = NULL;
592 }
593
594 /*
595 * ...and drain the rest of the pipelined responses
596 */
597 for (j = i + 1; j < (size_t)*pipelined; j++) {
598 redisReply *to_clear;
599
600 if (redisGetReply(conn->handle, (void **)&to_clear) != REDIS_OK) break;
601 fr_redis_reply_free(&to_clear);
602 }
603
604 out[0] = reply;
605
606 *rcode = status;
607 *pipelined = 0; /* all outstanding responses should be cleared */
608
609 return reply ? 1 : 0;
610 }
611 }
612
613 if (i != (size_t)*pipelined) {
614 fr_strerror_printf("Expected %u responses, got %zu", *pipelined, i);
615 status = REDIS_RCODE_ERROR;
616 goto error;
617 }
618
619 *rcode = status;
620
621 *pipelined = 0; /* all outstanding responses should be cleared */
622
623 return i;
624}
625
626/** Get the version of Redis running on the remote server
627 *
628 * This can be useful for some modules, as it allows adaptive behaviour, or early termination.
629 *
630 * @param[out] out Where to write the version string.
631 * @param[in] out_len Length of the version string buffer.
632 * @param[in] conn Used to query the version string.
633 * @return
634 * - #REDIS_RCODE_SUCCESS on success.
635 * - #REDIS_RCODE_ERROR on command/response mismatch or command error.
636 * - REDIS_RCODE_* on other errors;
637 */
639{
640 redisReply *reply;
641 fr_redis_rcode_t status;
642 char *p, *q;
643
644 fr_assert(out_len > 0);
645 out[0] = '\0';
646
647 reply = redisCommand(conn->handle, "INFO SERVER");
648 status = fr_redis_command_status(conn, reply);
649 if (status != REDIS_RCODE_SUCCESS) return status;
650
651 if (reply->type != REDIS_REPLY_STRING) {
652 fr_strerror_printf("Bad value type, expected string or integer, got %s",
653 fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
654 error:
655 fr_redis_reply_free(&reply);
656 return REDIS_RCODE_ERROR;
657 }
658
659 p = strstr(reply->str, "redis_version:");
660 if (!p) {
661 fr_strerror_const("Response did not contain version string");
662 goto error;
663 }
664
665 p = strchr(p, ':');
666 fr_assert(p);
667 p++;
668
669 q = strstr(p, "\r\n");
670 if (!q) q = p + strlen(p);
671
672 if ((size_t)(q - p) >= out_len) {
673 fr_strerror_printf("Version string %zu bytes, expected < %zu bytes", q - p, out_len);
674 goto error;
675 }
676 strlcpy(out, p, (q - p) + 1);
677
678 fr_redis_reply_free(&reply);
679
680 return REDIS_RCODE_SUCCESS;
681}
682
683/** Convert version string into a 32bit unsigned integer for comparisons
684 *
685 * @param[in] version string to parse.
686 * @return 32bit unsigned integer representing the version string.
687 */
688uint32_t fr_redis_version_num(char const *version)
689{
690 unsigned long num;
691 uint32_t ret;
692 char const *p = version;
693 char *q;
694
695 num = strtoul(p, &q, 10);
696 if (num > UINT8_MAX) {
697 fr_strerror_printf("Major version number %lu greater than " STRINGIFY(UINT8_MAX), num);
698 return 0;
699 }
700
701 if ((p == q) || (q[0] != '.')) {
702 fr_strerror_printf("Trailing garbage in Redis version \"%s\"", q);
703 return 0;
704 }
705 ret = num << 24;
706 p = q + 1;
707
708 num = strtoul(p, &q, 10);
709 if (num > UINT8_MAX) {
710 fr_strerror_printf("Minor version number %lu greater than " STRINGIFY(UINT8_MAX), num);
711 return 0;
712 }
713
714 if ((p == q) || (q[0] != '.')) {
715 fr_strerror_printf("Trailing garbage in Redis version \"%s\"", q);
716 return 0;
717 }
718 ret |= num << 16;
719 p = q + 1;
720
721 num = strtoul(p, &q, 10);
722 if (num > UINT16_MAX) {
723 fr_strerror_printf("Minor version number %lu greater than " STRINGIFY(UINT16_MAX), num);
724 return 0;
725 }
726
727 if ((p == q) || (q[0] != '\0')) {
728 fr_strerror_printf("Trailing garbage in Redis version \"%s\"", q);
729 return 0;
730 }
731
732 return ret | num;
733}
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define STRINGIFY(x)
Definition build.h:197
#define unlikely(_x)
Definition build.h:381
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
static fr_slen_t in
Definition dict.h:824
Test enumeration values.
Definition dict_test.h:92
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RDEBUGX(_l, fmt,...)
Definition log.h:340
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:498
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define DEBUGX(_lvl, _fmt,...)
Definition log.h:268
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
talloc_free(reap)
fr_log_lvl_t
Definition log.h:67
#define MAP_VERIFY(_x)
Definition map.h:108
@ TMPL_ATTR_REF_PREFIX_NO
Attribute refs have no '&' prefix.
unsigned short uint16_t
fr_slen_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix, fr_sbuff_escape_rules_t const *e_rules)
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
#define UINT8_MAX
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define INFO(fmt,...)
Definition radict.c:54
redisContext * handle
Hiredis context used when issuing commands.
Definition base.h:101
#define REDIS_ERROR_TRY_AGAIN_STR
Definition base.h:48
#define REDIS_ERROR_MOVED_STR
Definition base.h:46
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition base.h:64
#define REDIS_ERROR_ASK_STR
Definition base.h:47
#define REDIS_ERROR_NO_SCRIPT_STR
Definition base.h:49
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
Connection handle, holding a redis context.
Definition base.h:100
fr_table_num_sorted_t const redis_rcodes[]
Definition redis.c:40
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
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
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
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_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 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
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
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define tmpl_value_length(_tmpl)
Definition tmpl.h:949
#define tmpl_value(_tmpl)
Definition tmpl.h:948
int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
Create a tmpl_t from a fr_value_box_t.
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
#define tmpl_is_data(vpt)
Definition tmpl.h:211
#define tmpl_value_type(_tmpl)
Definition tmpl.h:950
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
@ T_INVALID
Definition token.h:39
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:554
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3352
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:3927
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4148
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition value.c:4232
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:621
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
#define FR_VALUE_BOX_INITIALISER_NULL(_vb)
A static initialiser for stack/globally allocated boxes.
Definition value.h:488
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition value.h:593
#define fr_value_box(_box, _var, _tainted)
Automagically fill in a box, determining the value type from the type of the C variable.
Definition value.h:871
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:632
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:587
static size_t char ** out
Definition value.h:997