diff options
Diffstat (limited to '')
-rw-r--r-- | src/modules/rlm_cache/serialize.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/modules/rlm_cache/serialize.c b/src/modules/rlm_cache/serialize.c new file mode 100644 index 0000000..b1f8e0c --- /dev/null +++ b/src/modules/rlm_cache/serialize.c @@ -0,0 +1,243 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file serialize.c + * @brief Serialize and deserialise cache entries. + * + * @author Arran Cudbard-Bell + * @copyright 2014 Arran Cudbard-Bell <a.cudbardb@freeradius.org> + * @copyright 2014 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "rlm_cache.h" +#include "serialize.h" + +/** Serialize a cache entry as a humanly readable string + * + * @param ctx to alloc new string in. Should be a talloc pool a little bigger + * than the maximum serialized size of the entry. + * @param out Where to write pointer to serialized cache entry. + * @param c Cache entry to serialize. + * @return 0 on success else -1. + */ +int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t *c) +{ + TALLOC_CTX *pairs = NULL; + + vp_cursor_t cursor; + VALUE_PAIR *vp; + + char *to_store = NULL, *pair; + + to_store = talloc_asprintf(ctx, "&Cache-Expires = %" PRIu64 "\n&Cache-Created = %" PRIu64 "\n", + (uint64_t)c->expires, (uint64_t)c->created); + if (!to_store) goto error; + + /* + * It's valid to have an empty cache entry (save allocing the + * pairs pool) + */ + if (!c->control && !c->packet && !c->reply) goto finish; + + /* + * In the majority of cases using these pools reduces the number of mallocs + * to two, except in the case where the total serialized pairs length is + * greater than the pairs pool, or the total serialized string is greater + * than the store pool. + */ + pairs = talloc_pool(ctx, 512); + if (!pairs) { + error: + talloc_free(pairs); + return -1; + } + + if (c->control) { + for (vp = fr_cursor_init(&cursor, &c->control); + vp; + vp = fr_cursor_next(&cursor)) { + pair = vp_aprints(pairs, vp, '\''); + if (!pair) goto error; + + to_store = talloc_asprintf_append_buffer(to_store, "&control:%s\n", pair); + if (!to_store) goto error; + } + } + + if (c->packet) { + for (vp = fr_cursor_init(&cursor, &c->packet); + vp; + vp = fr_cursor_next(&cursor)) { + pair = vp_aprints(pairs, vp, '\''); + if (!pair) goto error; + + to_store = talloc_asprintf_append_buffer(to_store, "&%s\n", pair); + if (!to_store) goto error; + } + } + + if (c->reply) { + for (vp = fr_cursor_init(&cursor, &c->reply); + vp; + vp = fr_cursor_next(&cursor)) { + pair = vp_aprints(pairs, vp, '\''); + if (!pair) goto error; + + to_store = talloc_asprintf_append_buffer(to_store, "&reply:%s\n", pair); + if (!to_store) goto error; + } + } + + if (c->state) { + for (vp = fr_cursor_init(&cursor, &c->state); + vp; + vp = fr_cursor_next(&cursor)) { + pair = vp_aprints(pairs, vp, '\''); + if (!pair) goto error; + + to_store = talloc_asprintf_append_buffer(to_store, "&session-state:%s\n", pair); + if (!to_store) goto error; + } + } + +finish: + talloc_free(pairs); + *out = to_store; + + return 0; +} + +/** Converts a serialized cache entry back into a structure + * + * @param c Cache entry to populate (should already be allocated) + * @param in String representation of cache entry. + * @param inlen Length of string. May be < 0 in which case strlen will be + * used to calculate the length of the string. + * @return 0 on success, -1 on error. + */ +int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen) +{ + vp_cursor_t packet, control, reply, state; + + TALLOC_CTX *store = NULL; + char *p, *q; + + store = talloc_pool(c, 1024); + if (!store) return -1; + + if (inlen < 0) inlen = strlen(in); + + fr_cursor_init(&packet, &c->packet); + fr_cursor_init(&control, &c->control); + fr_cursor_init(&reply, &c->reply); + fr_cursor_init(&state, &c->state); + + p = in; + + while (((size_t)(p - in)) < (size_t)inlen) { + vp_map_t *map = NULL; + VALUE_PAIR *vp = NULL; + ssize_t len; + + q = strchr(p, '\n'); + if (!q) break; /* List should also be terminated with a \n */ + *q = '\0'; + + if (map_afrom_attr_str(store, &map, p, + REQUEST_CURRENT, PAIR_LIST_REQUEST, + REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { + fr_strerror_printf("Failed parsing pair: %s", p); + goto error; + } + + if (map->lhs->type != TMPL_TYPE_ATTR) { + fr_strerror_printf("Pair left hand side \"%s\" parsed as %s, needed attribute. " + "Check local dictionaries", map->lhs->name, + fr_int2str(tmpl_names, map->lhs->type, "<INVALID>")); + goto error; + } + + if (map->rhs->type != TMPL_TYPE_LITERAL) { + fr_strerror_printf("Pair right hand side \"%s\" parsed as %s, needed literal. " + "Check serialized data quoting", map->rhs->name, + fr_int2str(tmpl_names, map->rhs->type, "<INVALID>")); + goto error; + } + + /* + * Convert literal to a type appropriate for the VP. + */ + if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) goto error; + + vp = fr_pair_afrom_da(c, map->lhs->tmpl_da); + len = value_data_copy(vp, &vp->data, map->rhs->tmpl_data_type, + &map->rhs->tmpl_data_value, map->rhs->tmpl_data_length); + if (len < 0) goto error; + vp->vp_length = len; + + /* + * Pull out the special attributes, and set the + * relevant cache entry fields. + */ + if (vp->da->vendor == 0) switch (vp->da->attr) { + case PW_CACHE_CREATED: + c->created = vp->vp_date; + talloc_free(vp); + goto next; + + case PW_CACHE_EXPIRES: + c->expires = vp->vp_date; + talloc_free(vp); + goto next; + + default: + break; + } + + switch (map->lhs->tmpl_list) { + case PAIR_LIST_REQUEST: + fr_cursor_insert(&packet, vp); + break; + + case PAIR_LIST_CONTROL: + fr_cursor_insert(&control, vp); + break; + + case PAIR_LIST_REPLY: + fr_cursor_insert(&reply, vp); + break; + + case PAIR_LIST_STATE: + fr_cursor_insert(&state, vp); + break; + + default: + fr_strerror_printf("Invalid cache list for pair: %s", p); + error: + talloc_free(vp); + talloc_free(map); + return -1; + } + next: + p = q + 1; + talloc_free(map); + } + + return 0; +} |