* Copyright 2000,2006 The FreeRADIUS server project
*/
-#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/rad_assert.h>
#include <ctype.h>
-#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
void *unique_ptr;
int unique_int;
void *opaque;
- void (*free_opaque)(void *);
+ bool free_opaque;
};
/*
* Add opaque data (with a "free" function) to a REQUEST.
*
- * The unique ptr is meant to be a malloc'd module configuration,
+ * The unique ptr is meant to be a module configuration,
* and the unique integer allows the caller to have multiple
* opaque data associated with a REQUEST.
*/
int request_data_add(REQUEST *request,
void *unique_ptr, int unique_int,
- void *opaque, void (*free_opaque)(void *))
+ void *opaque, bool free_opaque)
{
request_data_t *this, **last, *next;
next = this->next;
- if (this->opaque && /* free it, if necessary */
- this->free_opaque)
- this->free_opaque(this->opaque);
- break; /* replace the existing entry */
+ /*
+ * If caller requires custom behaviour on free
+ * they must set a destructor.
+ */
+ if (this->opaque && this->free_opaque) {
+ talloc_free(this->opaque);
+ }
+ break; /* replace the existing entry */
}
}
- if (!this) this = rad_malloc(sizeof(*this));
- memset(this, 0, sizeof(*this));
+ if (!this) this = talloc_zero(request, request_data_t);
this->next = next;
this->unique_ptr = unique_ptr;
this->unique_int = unique_int;
this->opaque = opaque;
- this->free_opaque = free_opaque;
+ if (free_opaque) {
+ this->free_opaque = free_opaque;
+ }
*last = this;
* Remove the entry from the list, and free it.
*/
*last = this->next;
- free(this);
- return ptr; /* don't free it, the caller does that */
+ talloc_free(this);
+ return ptr; /* don't free it, the caller does that */
}
}
return NULL; /* wasn't found, too bad... */
}
-
-/*
- * Free a REQUEST struct.
- */
-void request_free(REQUEST **request_ptr)
-{
- REQUEST *request;
-
- if ((request_ptr == NULL) || !*request_ptr)
- return;
-
- request = *request_ptr;
-
- rad_assert(!request->in_request_hash);
-#ifdef WITH_PROXY
- rad_assert(!request->in_proxy_hash);
-#endif
- rad_assert(!request->ev);
-
- if (request->packet)
- rad_free(&request->packet);
-
-#ifdef WITH_PROXY
- if (request->proxy)
- rad_free(&request->proxy);
-#endif
-
- if (request->reply)
- rad_free(&request->reply);
-
-#ifdef WITH_PROXY
- if (request->proxy_reply)
- rad_free(&request->proxy_reply);
-#endif
-
- if (request->config_items)
- pairfree(&request->config_items);
-
- request->username = NULL;
- request->password = NULL;
-
- if (request->data) {
- request_data_t *this, *next;
-
- for (this = request->data; this != NULL; this = next) {
- next = this->next;
- if (this->opaque && /* free it, if necessary */
- this->free_opaque)
- this->free_opaque(this->opaque);
- free(this);
- }
- request->data = NULL;
- }
-
- if (request->root &&
- (request->root->refcount > 0)) {
- request->root->refcount--;
- request->root = NULL;
- }
-
-#ifdef WITH_COA
- if (request->coa) {
- request->coa->parent = NULL;
- rad_assert(request->coa->ev == NULL);
- request_free(&request->coa);
- }
-
- if (request->parent && (request->parent->coa == request)) {
- request->parent->coa = NULL;
- }
-#endif
-
-#ifndef NDEBUG
- request->magic = 0x01020304; /* set the request to be nonsense */
-#endif
- request->client = NULL;
-#ifdef WITH_PROXY
- request->home_server = NULL;
-#endif
- free(request);
-
- *request_ptr = NULL;
-}
-
-/*
- * Check a filename for sanity.
- *
- * Allow only uppercase/lowercase letters, numbers, and '-_/.'
- */
-int rad_checkfilename(const char *filename)
-{
- if (strspn(filename, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.") == strlen(filename)) {
- return 0;
- }
-
- return -1;
-}
-
/*
* Create possibly many directories.
*
{
int rcode;
char *p;
- struct stat st;
/*
- * If the directory exists, don't do anything.
+ * Try to make the directory. If it exists, chmod it.
+ * If a path doesn't exist, that's OK. Otherwise
+ * return with an error.
*/
- if (stat(directory, &st) == 0) {
- return 0;
- }
+ rcode = mkdir(directory, mode & 0777);
+ if (rcode < 0) {
+ if (errno == EEXIST) {
+ return chmod(directory, mode);
+ }
- /*
- * Look for the LAST directory name. Try to create that,
- * failing on any error.
- */
- p = strrchr(directory, FR_DIR_SEP);
- if (p != NULL) {
- *p = '\0';
- rcode = rad_mkdir(directory, mode);
+ if (errno != ENOENT) {
+ return rcode;
+ }
/*
- * On error, we leave the directory name as the
- * one which caused the error.
+ * A component in the directory path doesn't
+ * exist. Look for the LAST directory name. Try
+ * to create that. If there's an error, we leave
+ * the directory path as the one at which the
+ * error occured.
*/
- if (rcode < 0) {
- if (errno == EEXIST) return 0;
- return rcode;
- }
+ p = strrchr(directory, FR_DIR_SEP);
+ if (!p || (p == directory)) return -1;
+
+ *p = '\0';
+ rcode = rad_mkdir(directory, mode);
+ if (rcode < 0) return rcode;
/*
- * Reset the directory delimiter, and go ask
- * the system to make the directory.
+ * Reset the directory path, and try again to
+ * make the directory.
*/
*p = FR_DIR_SEP;
- } else {
- return 0;
- }
+ rcode = mkdir(directory, mode & 0777);
+ if (rcode < 0) return rcode;
+ } /* else we successfully created the directory */
- /*
- * Having done everything successfully, we do the
- * system call to actually go create the directory.
- */
- rcode = mkdir(directory, mode & 0777);
- if (rcode < 0) {
- return rcode;
- }
-
- /*
- * Set things like sticky bits that aren't supported by
- * mkdir.
- */
- if (mode & ~0777) {
- rcode = chmod(directory, mode);
- }
-
- return rcode;
+ return chmod(directory, mode);
}
/*
- * Module malloc() call, which does stuff if the malloc fails.
+ * Allocate memory, or exit.
*
* This call ALWAYS succeeds!
*/
void *ptr = malloc(size);
if (ptr == NULL) {
- radlog(L_ERR, "no memory");
- exit(1);
+ ERROR("no memory");
+ fr_exit(1);
}
return ptr;
}
-void *rad_calloc(size_t size)
-{
- void *ptr = rad_malloc(size);
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Signature for free is dumb, and raises errors when we try
- * to free const ptrs.
- */
-void rad_cfree(const void *ptr)
+void rad_const_free(void const *ptr)
{
void *tmp;
if (!ptr) return;
memcpy(&tmp, &ptr, sizeof(tmp));
- free(tmp);
+ talloc_free(tmp);
}
+
/*
* Logs an error message and aborts the program
*
*/
-void NEVER_RETURNS rad_assert_fail (const char *file, unsigned int line,
- const char *expr)
+void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
{
- radlog(L_ERR, "ASSERT FAILED %s[%u]: %s", file, line, expr);
- abort();
+ ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
+ fr_fault(SIGABRT);
+ fr_exit_now(1);
}
+/*
+ * Free a REQUEST struct.
+ */
+static int _request_free(REQUEST *request)
+{
+ rad_assert(!request->in_request_hash);
+#ifdef WITH_PROXY
+ rad_assert(!request->in_proxy_hash);
+#endif
+ rad_assert(!request->ev);
+
+#ifdef WITH_COA
+ if (request->coa) {
+ request->coa->parent = NULL;
+ }
+
+ if (request->parent && (request->parent->coa == request)) {
+ request->parent->coa = NULL;
+ }
+#endif
+
+#ifndef NDEBUG
+ request->magic = 0x01020304; /* set the request to be nonsense */
+#endif
+ request->client = NULL;
+#ifdef WITH_PROXY
+ request->home_server = NULL;
+#endif
+
+ return 0;
+}
/*
* Create a new REQUEST data structure.
*/
-REQUEST *request_alloc(void)
+REQUEST *request_alloc(TALLOC_CTX *ctx)
{
REQUEST *request;
- request = rad_malloc(sizeof(REQUEST));
- memset(request, 0, sizeof(REQUEST));
+ request = talloc_zero(ctx, REQUEST);
+ talloc_set_destructor(request, _request_free);
#ifndef NDEBUG
request->magic = REQUEST_MAGIC;
#endif
request->username = NULL;
request->password = NULL;
request->timestamp = time(NULL);
- request->options = debug_flag; /* Default to global debug level */
+ request->log.lvl = debug_flag; /* Default to global debug level */
request->module = "";
request->component = "<core>";
- if (debug_flag) request->radlog = radlog_request;
+ request->log.func = vradlog_request;
return request;
}
*/
REQUEST *request_alloc_fake(REQUEST *request)
{
- REQUEST *fake;
+ REQUEST *fake;
- fake = request_alloc();
+ fake = request_alloc(request);
- fake->number = request->number;
+ fake->number = request->number;
#ifdef HAVE_PTHREAD_H
- fake->child_pid = request->child_pid;
+ fake->child_pid = request->child_pid;
#endif
- fake->parent = request;
- fake->root = request->root;
- fake->client = request->client;
-
- /*
- * For new server support.
- *
- * FIXME: Key instead off of a "virtual server" data structure.
- *
- * FIXME: Permit different servers for inner && outer sessions?
- */
- fake->server = request->server;
-
- fake->packet = rad_alloc(1);
- if (!fake->packet) {
- request_free(&fake);
- return NULL;
- }
-
- fake->reply = rad_alloc(0);
- if (!fake->reply) {
- request_free(&fake);
- return NULL;
- }
-
- fake->master_state = REQUEST_ACTIVE;
- fake->child_state = REQUEST_RUNNING;
-
- /*
- * Fill in the fake request.
- */
- fake->packet->sockfd = -1;
- fake->packet->src_ipaddr = request->packet->src_ipaddr;
- fake->packet->src_port = request->packet->src_port;
- fake->packet->dst_ipaddr = request->packet->dst_ipaddr;
- fake->packet->dst_port = 0;
-
- /*
- * This isn't STRICTLY required, as the fake request MUST NEVER
- * be put into the request list. However, it's still reasonable
- * practice.
- */
- fake->packet->id = fake->number & 0xff;
- fake->packet->code = request->packet->code;
- fake->timestamp = request->timestamp;
-
- /*
- * Required for new identity support
- */
- fake->listener = request->listener;
-
- /*
- * Fill in the fake reply, based on the fake request.
- */
- fake->reply->sockfd = fake->packet->sockfd;
- fake->reply->src_ipaddr = fake->packet->dst_ipaddr;
- fake->reply->src_port = fake->packet->dst_port;
- fake->reply->dst_ipaddr = fake->packet->src_ipaddr;
- fake->reply->dst_port = fake->packet->src_port;
- fake->reply->id = fake->packet->id;
- fake->reply->code = 0; /* UNKNOWN code */
-
- /*
- * Copy debug information.
- */
- fake->options = request->options;
- fake->radlog = request->radlog;
-
- return fake;
+ fake->parent = request;
+ fake->root = request->root;
+ fake->client = request->client;
+
+ /*
+ * For new server support.
+ *
+ * FIXME: Key instead off of a "virtual server" data structure.
+ *
+ * FIXME: Permit different servers for inner && outer sessions?
+ */
+ fake->server = request->server;
+
+ fake->packet = rad_alloc(fake, true);
+ if (!fake->packet) {
+ talloc_free(fake);
+ return NULL;
+ }
+
+ fake->reply = rad_alloc(fake, false);
+ if (!fake->reply) {
+ talloc_free(fake);
+ return NULL;
+ }
+
+ fake->master_state = REQUEST_ACTIVE;
+ fake->child_state = REQUEST_RUNNING;
+
+ /*
+ * Fill in the fake request.
+ */
+ fake->packet->sockfd = -1;
+ fake->packet->src_ipaddr = request->packet->src_ipaddr;
+ fake->packet->src_port = request->packet->src_port;
+ fake->packet->dst_ipaddr = request->packet->dst_ipaddr;
+ fake->packet->dst_port = 0;
+
+ /*
+ * This isn't STRICTLY required, as the fake request MUST NEVER
+ * be put into the request list. However, it's still reasonable
+ * practice.
+ */
+ fake->packet->id = fake->number & 0xff;
+ fake->packet->code = request->packet->code;
+ fake->timestamp = request->timestamp;
+ fake->packet->timestamp = request->packet->timestamp;
+
+ /*
+ * Required for new identity support
+ */
+ fake->listener = request->listener;
+
+ /*
+ * Fill in the fake reply, based on the fake request.
+ */
+ fake->reply->sockfd = fake->packet->sockfd;
+ fake->reply->src_ipaddr = fake->packet->dst_ipaddr;
+ fake->reply->src_port = fake->packet->dst_port;
+ fake->reply->dst_ipaddr = fake->packet->src_ipaddr;
+ fake->reply->dst_port = fake->packet->src_port;
+ fake->reply->id = fake->packet->id;
+ fake->reply->code = 0; /* UNKNOWN code */
+
+ /*
+ * Copy debug information.
+ */
+ memcpy(&(fake->log), &(request->log), sizeof(fake->log));
+
+ return fake;
}
#ifdef WITH_COA
/*
* Originate CoA requests only when necessary.
*/
- if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
- (request->packet->code != PW_ACCOUNTING_REQUEST)) return NULL;
+ if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
+ (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
request->coa = request_alloc_fake(request);
if (!request->coa) return NULL;
request->coa->packet->code = 0; /* unknown, as of yet */
request->coa->child_state = REQUEST_RUNNING;
- request->coa->proxy = rad_alloc(0);
+ request->coa->proxy = rad_alloc(request->coa, false);
if (!request->coa->proxy) {
- request_free(&request->coa);
+ TALLOC_FREE(request->coa);
return NULL;
}
/*
* Copy a quoted string.
*/
-int rad_copy_string(char *to, const char *from)
+int rad_copy_string(char *to, char const *from)
{
int length = 0;
char quote = *from;
return length;
}
+/*
+ * Copy a quoted string but without the quotes. The length
+ * returned is the number of chars written; the number of
+ * characters consumed is 2 more than this.
+ */
+int rad_copy_string_bare(char *to, char const *from)
+{
+ int length = 0;
+ char quote = *from;
+
+ from++;
+ while (*from && (*from != quote)) {
+ if (*from == '\\') {
+ *(to++) = *(from++);
+ length++;
+ }
+ *(to++) = *(from++);
+ length++;
+ }
+
+ if (*from != quote) return -1; /* not properly quoted */
+
+ *to = '\0';
+
+ return length;
+}
+
/*
* Copy a %{} string.
*/
-int rad_copy_variable(char *to, const char *from)
+int rad_copy_variable(char *to, char const *from)
{
int length = 0;
int sublen;
#define USEC 1000000
#endif
-int rad_pps(int *past, int *present, time_t *then, struct timeval *now)
+uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
{
- int pps;
+ uint32_t pps;
if (*then != now->tv_sec) {
*then = now->tv_sec;
return pps;
}
+
+/** Split string into words and expand each one
+ *
+ * @param request Current request.
+ * @param cmd string to split.
+ * @param max_argc the maximum number of arguments to split into.
+ * @param argv Where to write the pointers into argv_buf.
+ * @param can_fail If false, stop processing if any of the xlat expansions fail.
+ * @param argv_buflen size of argv_buf.
+ * @param argv_buf temporary buffer we used to mangle/expand cmd.
+ * Pointers to offsets of this buffer will be written to argv.
+ * @return argc or -1 on failure.
+ */
+
+int rad_expand_xlat(REQUEST *request, char const *cmd,
+ int max_argc, char *argv[], bool can_fail,
+ size_t argv_buflen, char *argv_buf)
+{
+ char const *from;
+ char *to;
+ int argc = -1;
+ int i;
+ int left;
+
+ if (strlen(cmd) > (argv_buflen - 1)) {
+ ERROR("rad_expand_xlat: Command line is too long");
+ return -1;
+ }
+
+ /*
+ * Check for bad escapes.
+ */
+ if (cmd[strlen(cmd) - 1] == '\\') {
+ ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
+ return -1;
+ }
+
+ strlcpy(argv_buf, cmd, argv_buflen);
+
+ /*
+ * Split the string into argv's BEFORE doing radius_xlat...
+ */
+ from = cmd;
+ to = argv_buf;
+ argc = 0;
+ while (*from) {
+ int length;
+
+ /*
+ * Skip spaces.
+ */
+ if ((*from == ' ') || (*from == '\t')) {
+ from++;
+ continue;
+ }
+
+ argv[argc] = to;
+ argc++;
+
+ if (argc >= (max_argc - 1)) break;
+
+ /*
+ * Copy the argv over to our buffer.
+ */
+ while (*from && (*from != ' ') && (*from != '\t')) {
+ if (to >= argv_buf + argv_buflen - 1) {
+ ERROR("rad_expand_xlat: Ran out of space in command line");
+ return -1;
+ }
+
+ switch (*from) {
+ case '"':
+ case '\'':
+ length = rad_copy_string_bare(to, from);
+ if (length < 0) {
+ ERROR("rad_expand_xlat: Invalid string passed as argument");
+ return -1;
+ }
+ from += length+2;
+ to += length;
+ break;
+
+ case '%':
+ if (from[1] == '{') {
+ *(to++) = *(from++);
+
+ length = rad_copy_variable(to, from);
+ if (length < 0) {
+ ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
+ return -1;
+ }
+ from += length;
+ to += length;
+ } else { /* FIXME: catch %%{ ? */
+ *(to++) = *(from++);
+ }
+ break;
+
+ case '\\':
+ if (from[1] == ' ') from++;
+ /* FALL-THROUGH */
+
+ default:
+ *(to++) = *(from++);
+ }
+ } /* end of string, or found a space */
+
+ *(to++) = '\0'; /* terminate the string */
+ }
+
+ /*
+ * We have to have SOMETHING, at least.
+ */
+ if (argc <= 0) {
+ ERROR("rad_expand_xlat: Empty command line");
+ return -1;
+ }
+
+ /*
+ * Expand each string, as appropriate.
+ */
+ left = argv_buf + argv_buflen - to;
+ for (i = 0; i < argc; i++) {
+ int sublen;
+
+ /*
+ * Don't touch argv's which won't be translated.
+ */
+ if (strchr(argv[i], '%') == NULL) continue;
+
+ if (!request) continue;
+
+ sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
+ if (sublen <= 0) {
+ if (can_fail) {
+ /*
+ * Fail to be backwards compatible.
+ *
+ * It's yucky, but it won't break anything,
+ * and it won't cause security problems.
+ */
+ sublen = 0;
+ } else {
+ ERROR("rad_expand_xlat: xlat failed");
+ return -1;
+ }
+ }
+
+ argv[i] = to;
+ to += sublen;
+ *(to++) = '\0';
+ left -= sublen;
+ left--;
+
+ if (left <= 0) {
+ ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
+ return -1;
+ }
+ }
+ argv[argc] = NULL;
+
+ return argc;
+}
+
+const FR_NAME_NUMBER pair_lists[] = {
+ { "request", PAIR_LIST_REQUEST },
+ { "reply", PAIR_LIST_REPLY },
+ { "control", PAIR_LIST_CONTROL }, /* New name should have priority */
+ { "config", PAIR_LIST_CONTROL },
+#ifdef WITH_PROXY
+ { "proxy-request", PAIR_LIST_PROXY_REQUEST },
+ { "proxy-reply", PAIR_LIST_PROXY_REPLY },
+#endif
+#ifdef WITH_COA
+ { "coa", PAIR_LIST_COA },
+ { "coa-reply", PAIR_LIST_COA_REPLY },
+ { "disconnect", PAIR_LIST_DM },
+ { "disconnect-reply", PAIR_LIST_DM_REPLY },
+#endif
+ { NULL , -1 }
+};
+
+const FR_NAME_NUMBER request_refs[] = {
+ { "outer", REQUEST_OUTER },
+ { "current", REQUEST_CURRENT },
+ { "parent", REQUEST_PARENT },
+ { NULL , -1 }
+};
+
+
+/** Resolve attribute name to a list.
+ *
+ * Check the name string for qualifiers that specify a list and return
+ * an pair_lists_t value for that list. This value may be passed to
+ * radius_list, along with the current request, to get a pointer to the
+ * actual list in the request.
+ *
+ * If qualifiers were consumed, write a new pointer into name to the
+ * char after the last qualifier to be consumed.
+ *
+ * radius_list_name should be called before passing a name string that
+ * may contain qualifiers to dict_attrbyname.
+ *
+ * @see dict_attrbyname
+ *
+ * @param[in,out] name of attribute.
+ * @param[in] default_list the list to return if no qualifiers were found.
+ * @return PAIR_LIST_UNKOWN if qualifiers couldn't be resolved to a list.
+ */
+pair_lists_t radius_list_name(char const **name, pair_lists_t default_list)
+{
+ char const *p = *name;
+ char const *q;
+ pair_lists_t output;
+
+ /* This should never be a NULL pointer or zero length string */
+ rad_assert(name && *name);
+
+ /*
+ * Unfortunately, ':' isn't a definitive separator for
+ * the list name. We may have numeric tags, too.
+ */
+ q = strchr(p, ':');
+ if (q) {
+ /*
+ * Check for tagged attributes. They have
+ * "name:tag", where tag is a decimal number.
+ * Valid tags are invalid attributes, so that's
+ * OK.
+ *
+ * Also allow "name:tag[#]" as a tag.
+ *
+ * However, "request:" is allowed, too, and
+ * shouldn't be interpreted as a tag.
+ *
+ * We do this check first rather than just
+ * looking up the request name, because this
+ * check is cheap, and looking up the request
+ * name is expensive.
+ */
+ if (isdigit((int) q[1])) {
+ char const *d = q + 1;
+
+ while (isdigit((int) *d)) {
+ d++;
+ }
+
+ /*
+ * Return the DEFAULT list as supplied by
+ * the caller. This is usually
+ * PAIRLIST_REQUEST.
+ */
+ if (!*d || (*d == '[')) {
+ return default_list;
+ }
+ }
+
+ /*
+ * If the first part is a list name, then treat
+ * it as a list. This means that we CANNOT have
+ * an attribute which is named "request",
+ * "reply", etc. Allowing a tagged attribute
+ * "request:3" would just be insane.
+ */
+ output = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+ if (output != PAIR_LIST_UNKNOWN) {
+ *name = (q + 1); /* Consume the list and delimiter */
+ return output;
+ }
+
+ /*
+ * It's not a known list, say so.
+ */
+ return PAIR_LIST_UNKNOWN;
+ }
+
+ /*
+ * The input string may be just a list name,
+ * e.g. "request". Check for that.
+ */
+ q = (p + strlen(p));
+ output = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+ if (output != PAIR_LIST_UNKNOWN) {
+ *name = q;
+ return output;
+ }
+
+ /*
+ * It's just an attribute name. Return the default list
+ * as supplied by the caller.
+ */
+ return default_list;
+}
+
+
+/** Resolve attribute name to a request.
+ *
+ * Check the name string for qualifiers that reference a parent request and
+ * write the pointer to this request to 'request'.
+ *
+ * If qualifiers were consumed, write a new pointer into name to the
+ * char after the last qualifier to be consumed.
+ *
+ * radius_ref_request should be called before radius_list_name.
+ *
+ * @see radius_list_name
+ * @param[in,out] name of attribute.
+ * @param[in] def default request ref to return if no request qualifier is present.
+ * @return one of the REQUEST_* definitions or REQUEST_UNKOWN
+ */
+request_refs_t radius_request_name(char const **name, request_refs_t def)
+{
+ char *p;
+ int request;
+
+ p = strchr(*name, '.');
+ if (!p) {
+ return def;
+ }
+
+ /*
+ * We may get passed "127.0.0.1".
+ */
+ request = fr_substr2int(request_refs, *name, REQUEST_UNKNOWN,
+ p - *name);
+
+ /*
+ * If we get a valid name, skip it.
+ */
+ if (request != REQUEST_UNKNOWN) {
+ *name = p + 1;
+ return request;
+ }
+
+ /*
+ * Otherwise leave it alone, and return the caller's
+ * default.
+ */
+ return def;
+}
+
+/** Resolve request to a request.
+ *
+ * Resolve name to a current request.
+ *
+ * @see radius_list
+ * @param[in,out] context Base context to use, and to write the result back to.
+ * @param[in] name (request) to resolve to.
+ * @return 0 if request is valid in this context, else -1.
+ */
+int radius_request(REQUEST **context, request_refs_t name)
+{
+ REQUEST *request = *context;
+
+ switch (name) {
+ case REQUEST_CURRENT:
+ return 0;
+
+ case REQUEST_PARENT: /* for future use in request chaining */
+ case REQUEST_OUTER:
+ if (!request->parent) {
+ return -1;
+ }
+ *context = request->parent;
+ break;
+
+ case REQUEST_UNKNOWN:
+ default:
+ rad_assert(0);
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Adds subcapture values to request data
+ *
+ * Allows use of %{n} expansions.
+ *
+ * @param request Current request.
+ * @param compare Result returned by regexec.
+ * @param value The original value.
+ * @param rxmatch Pointers into value.
+ */
+void rad_regcapture(REQUEST *request, int compare, char const *value, regmatch_t rxmatch[])
+{
+ int i;
+ char *p;
+ size_t len;
+
+ if (compare == REG_NOMATCH) {
+ return;
+ }
+
+ /*
+ * Add new %{0}, %{1}, etc.
+ */
+ for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+ /*
+ * Didn't match: delete old match, if it existed.
+ */
+ if (rxmatch[i].rm_so == -1) {
+ p = request_data_get(request, request, REQUEST_DATA_REGEX | i);
+ if (p) {
+ RDEBUG4("%%{%i}: Clearing old value \"%s\"", i, p);
+ talloc_free(p);
+ } else {
+ RDEBUG4("%%{%i}: Was empty", i);
+ }
+
+ continue;
+ }
+
+ len = rxmatch[i].rm_eo - rxmatch[i].rm_so;
+ p = talloc_array(request, char, len + 1);
+ if (!p) {
+ ERROR("Out of memory");
+ return;
+ }
+
+ memcpy(p, value + rxmatch[i].rm_so, len);
+ p[len] = '\0';
+
+ RDEBUG4("%%{%i}: Inserting new value \"%s\"", i, p);
+ /*
+ * Copy substring, and add it to
+ * the request.
+ *
+ * Note that we don't check
+ * for out of memory, which is
+ * the only error we can get...
+ */
+ request_data_add(request, request, REQUEST_DATA_REGEX | i, p, true);
+ }
+}
+
+#ifndef NDEBUG
+/*
+ * Verify a packet.
+ */
+static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
+{
+ TALLOC_CTX *parent;
+
+ if (!packet) {
+ fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: RADIUS_PACKET %s pointer was NULL", file, line, type);
+ fr_assert(0);
+ fr_exit_now(0);
+ }
+
+ parent = talloc_parent(packet);
+ if (parent != request) {
+ ERROR("CONSISTENCY CHECK FAILED %s[%u]: Expected RADIUS_PACKET %s to be parented by %p (%s), "
+ "but parented by %p (%s)", file, line, type, request, talloc_get_name(request),
+ parent, parent ? talloc_get_name(parent) : "NULL");
+
+ fr_log_talloc_report(packet);
+ if (parent) fr_log_talloc_report(parent);
+
+ rad_assert(0);
+ }
+
+ VERIFY_PACKET(packet);
+
+ if (!packet->vps) return;
+
+#ifdef WITH_VERIFY_PTR
+ fr_verify_list(file, line, packet, packet->vps);
+#endif
+}
+/*
+ * Catch horrible talloc errors.
+ */
+void verify_request(char const *file, int line, REQUEST *request)
+{
+ if (!request) {
+ fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: REQUEST pointer was NULL", file, line);
+ fr_assert(0);
+ fr_exit_now(0);
+ }
+
+ (void) talloc_get_type_abort(request, REQUEST);
+
+#ifdef WITH_VERIFY_PTR
+ fr_verify_list(file, line, request, request->config_items);
+#endif
+
+ if (request->packet) verify_packet(file, line, request, request->packet, "request");
+ if (request->reply) verify_packet(file, line, request, request->reply, "reply");
+#ifdef WITH_PROXY
+ if (request->proxy) verify_packet(file, line, request, request->proxy, "proxy-request");
+ if (request->proxy_reply) verify_packet(file, line, request, request->proxy_reply, "proxy-reply");
+#endif
+
+#ifdef WITH_COA
+ if (request->coa) {
+ void *parent;
+
+ (void) talloc_get_type_abort(request->coa, REQUEST);
+ parent = talloc_parent(request->coa);
+
+ rad_assert(parent == request);
+
+ verify_request(file, line, request->coa);
+ }
+#endif
+}
+#endif