newvector should be a bool
[freeradius.git] / src / main / util.c
index 7126819..c9d8b23 100644 (file)
@@ -26,7 +26,6 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #include <ctype.h>
-#include <signal.h>
 
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -83,19 +82,9 @@ struct request_data_t {
        void            *unique_ptr;
        int             unique_int;
        void            *opaque;
-       void            (*free_opaque)(void *);
+       bool            free_opaque;
 };
 
-static int request_data_free_opaque(void *ctx)
-{
-       request_data_t *this;
-
-       this = talloc_get_type_abort(ctx, request_data_t);
-       this->free_opaque(this->opaque);
-
-       return 0;
-}
-
 /*
  *     Add opaque data (with a "free" function) to a REQUEST.
  *
@@ -105,7 +94,7 @@ static int request_data_free_opaque(void *ctx)
  */
 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;
 
@@ -122,11 +111,14 @@ int request_data_add(REQUEST *request,
 
                        next = this->next;
 
-                       if (this->opaque && /* free it, if necessary */
-                           this->free_opaque) {
-                               this->free_opaque(this->opaque);
+                       /*
+                        *      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 */
+                       break;                          /* replace the existing entry */
                }
        }
 
@@ -136,10 +128,8 @@ int request_data_add(REQUEST *request,
        this->unique_ptr = unique_ptr;
        this->unique_int = unique_int;
        this->opaque = opaque;
-
        if (free_opaque) {
                this->free_opaque = free_opaque;
-               talloc_set_destructor((void *) this, request_data_free_opaque);
        }
 
        *last = this;
@@ -168,9 +158,8 @@ void *request_data_get(REQUEST *request,
                         *      Remove the entry from the list, and free it.
                         */
                        *last = this->next;
-                       talloc_set_destructor((void *) this, NULL);
                        talloc_free(this);
-                       return ptr; /* don't free it, the caller does that */
+                       return ptr;             /* don't free it, the caller does that */
                }
        }
 
@@ -199,87 +188,6 @@ void *request_data_reference(REQUEST *request,
        return NULL;            /* wasn't found, too bad... */
 }
 
-
-/*
- *     Free a REQUEST struct.
- */
-void request_free(REQUEST **request_ptr)
-{
-       REQUEST *request;
-
-       if (!request_ptr || !*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);
-
-#ifdef WITH_COA
-       if (request->coa) {
-               request->coa->parent = 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
-       talloc_free(request);
-       *request_ptr = NULL;
-}
-
-/*
- *     Check a filename for sanity.
- *
- *     Allow only uppercase/lowercase letters, numbers, and '-_/.'
- */
-int rad_checkfilename(char const *filename)
-{
-       if (strspn(filename, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.") == strlen(filename)) {
-               return 0;
-       }
-
-       return -1;
-}
-
-/** Check if file exists
- *
- * @param filename to check.
- * @return 0 if the file does not exist, 1 if the file exists, -1 if the file
- *     exists but there was an error opening it. errno value should be usable
- *     for error messages.
- */
-int rad_file_exists(char const *filename)
-{
-       int des;
-       int ret = 1;
-
-       if ((des = open(filename, O_RDONLY)) == -1) {
-               if (errno == ENOENT) {
-                       ret = 0;
-               } else {
-                       ret = -1;
-               }
-       } else {
-               close(des);
-       }
-
-       return ret;
-}
-
 /*
  *     Create possibly many directories.
  *
@@ -291,60 +199,46 @@ int rad_mkdir(char *directory, mode_t mode)
 {
        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;
-       }
-
-       /*
-        *      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);
+       rcode = mkdir(directory, mode & 0777);
+       if (rcode < 0) {
+               if (errno == EEXIST) {
+                       return chmod(directory, mode);
+               }
 
-               /*
-                *      On error, we leave the directory name as the
-                *      one which caused the error.
-                */
-               if (rcode < 0) {
-                       if (errno == EEXIST) return 0;
+               if (errno != ENOENT) {
                        return rcode;
                }
 
                /*
-                *      Reset the directory delimiter, and go ask
-                *      the system to make the directory.
+                *      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.
                 */
-               *p = FR_DIR_SEP;
-       } else {
-               return 0;
-       }
+               p = strrchr(directory, FR_DIR_SEP);
+               if (!p || (p == directory)) return -1;
 
-       /*
-        *      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;
-       }
+               *p = '\0';
+               rcode = rad_mkdir(directory, mode);
+               if (rcode < 0) return rcode;
 
-       /*
-        *      Set things like sticky bits that aren't supported by
-        *      mkdir.
-        */
-       if (mode & ~0777) {
-               rcode = chmod(directory, mode);
-       }
+               /*
+                *      Reset the directory path, and try again to
+                *      make the directory.
+                */
+               *p = FR_DIR_SEP;
+               rcode = mkdir(directory, mode & 0777);
+               if (rcode < 0) return rcode;
+       } /* else we successfully created the directory */
 
-       return rcode;
+       return chmod(directory, mode);
 }
 
 
@@ -359,20 +253,13 @@ void *rad_malloc(size_t size)
 
        if (ptr == NULL) {
                ERROR("no memory");
-               exit(1);
+               fr_exit(1);
        }
 
        return ptr;
 }
 
 
-void *rad_calloc(size_t size)
-{
-       void *ptr = rad_malloc(size);
-       memset(ptr, 0, size);
-       return ptr;
-}
-
 void rad_const_free(void const *ptr)
 {
        void *tmp;
@@ -388,13 +275,44 @@ void rad_const_free(void const *ptr)
  *
  */
 
-void NEVER_RETURNS rad_assert_fail (char const *file, unsigned int line,
-                                   char const *expr)
+void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
 {
        ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
-       abort();
+       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.
@@ -404,6 +322,7 @@ REQUEST *request_alloc(TALLOC_CTX *ctx)
        REQUEST *request;
 
        request = talloc_zero(ctx, REQUEST);
+       talloc_set_destructor(request, _request_free);
 #ifndef NDEBUG
        request->magic = REQUEST_MAGIC;
 #endif
@@ -418,11 +337,11 @@ REQUEST *request_alloc(TALLOC_CTX *ctx)
        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>";
-       request->radlog = radlog_request;
+       request->log.func = vradlog_request;
 
        return request;
 }
@@ -457,15 +376,15 @@ REQUEST *request_alloc_fake(REQUEST *request)
         */
        fake->server = request->server;
 
-       fake->packet = rad_alloc(request, 1);
+       fake->packet = rad_alloc(fake, true);
        if (!fake->packet) {
-               request_free(&fake);
+               talloc_free(fake);
                return NULL;
        }
 
-       fake->reply = rad_alloc(request, 0);
+       fake->reply = rad_alloc(fake, false);
        if (!fake->reply) {
-               request_free(&fake);
+               talloc_free(fake);
                return NULL;
        }
 
@@ -489,6 +408,7 @@ REQUEST *request_alloc_fake(REQUEST *request)
        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
@@ -509,8 +429,7 @@ REQUEST *request_alloc_fake(REQUEST *request)
        /*
         *      Copy debug information.
         */
-       fake->options = request->options;
-       fake->radlog = request->radlog;
+       memcpy(&(fake->log), &(request->log), sizeof(fake->log));
 
        return fake;
 }
@@ -523,17 +442,17 @@ REQUEST *request_alloc_coa(REQUEST *request)
        /*
         *      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(request->coa, 0);
+       request->coa->proxy = rad_alloc(request->coa, false);
        if (!request->coa->proxy) {
-               request_free(&request->coa);
+               TALLOC_FREE(request->coa);
                return NULL;
        }
 
@@ -661,9 +580,9 @@ int rad_copy_variable(char *to, char const *from)
 #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;
@@ -811,7 +730,7 @@ int rad_expand_xlat(REQUEST *request, char const *cmd,
         *      We have to have SOMETHING, at least.
         */
        if (argc <= 0) {
-               ERROR("rad_expand_xlat: Empty command line.");
+               ERROR("rad_expand_xlat: Empty command line");
                return -1;
        }
 
@@ -852,7 +771,7 @@ int rad_expand_xlat(REQUEST *request, char const *cmd,
                left--;
 
                if (left <= 0) {
-                       ERROR("rad_expand_xlat: Ran out of space while expanding arguments.");
+                       ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
                        return -1;
                }
        }
@@ -864,8 +783,8 @@ int rad_expand_xlat(REQUEST *request, char const *cmd,
 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 },
-       { "control",            PAIR_LIST_CONTROL },
 #ifdef WITH_PROXY
        { "proxy-request",      PAIR_LIST_PROXY_REQUEST },
        { "proxy-reply",        PAIR_LIST_PROXY_REPLY },
@@ -903,10 +822,10 @@ const FR_NAME_NUMBER request_refs[] = {
  * @see dict_attrbyname
  *
  * @param[in,out] name of attribute.
- * @param[in] unknown the list to return if no qualifiers were found.
+ * @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 unknown)
+pair_lists_t radius_list_name(char const **name, pair_lists_t default_list)
 {
        char const *p = *name;
        char const *q;
@@ -916,34 +835,79 @@ pair_lists_t radius_list_name(char const **name, pair_lists_t unknown)
        rad_assert(name && *name);
 
        /*
-        *      We couldn't determine the list if:
-        *
-        *      A colon delimiter was found, but the next char was a
-        *      number, indicating a tag, not a list qualifier.
-        *
-        *      No colon was found and the first char was upper case
-        *      indicating an attribute.
-        *
+        *      Unfortunately, ':' isn't a definitive separator for
+        *      the list name.  We may have numeric tags, too.
         */
        q = strchr(p, ':');
-       if (((q && (q[1] >= '0') && (q[1] <= '9'))) ||
-           (!q && isupper((int) *p))) {
-               return unknown;
-       }
-
        if (q) {
-               *name = (q + 1);        /* Consume the list and delimiter */
-               return fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+               /*
+                *      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;
        }
 
-       q = (p + strlen(p));    /* Consume the entire string */
+       /*
+        *      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;
        }
 
-       return unknown;
+       /*
+        *      It's just an attribute name.  Return the default list
+        *      as supplied by the caller.
+        */
+       return default_list;
 }
 
 
@@ -969,7 +933,7 @@ request_refs_t radius_request_name(char const **name, request_refs_t def)
 
        p = strchr(*name, '.');
        if (!p) {
-               return REQUEST_CURRENT;
+               return def;
        }
 
        /*
@@ -979,7 +943,7 @@ request_refs_t radius_request_name(char const **name, request_refs_t def)
                                p - *name);
 
        /*
-        *      If we get a VALID LIST, skip it.
+        *      If we get a valid name, skip it.
         */
        if (request != REQUEST_UNKNOWN) {
                *name = p + 1;
@@ -1013,13 +977,9 @@ int radius_request(REQUEST **context, request_refs_t name)
                case REQUEST_PARENT:    /* for future use in request chaining */
                case REQUEST_OUTER:
                        if (!request->parent) {
-                               REDEBUG("Specified request \"%s\" is not available in this context",
-                                       fr_int2str(request_refs, name, "<INVALID>"));
                                return -1;
                        }
-
                        *context = request->parent;
-
                        break;
 
                case REQUEST_UNKNOWN:
@@ -1031,3 +991,136 @@ int radius_request(REQUEST **context, request_refs_t name)
        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