示例#1
0
文件: pcre.c 项目: niubl/ironbee
/**
 * Internal compilation of the modpcre pattern.
 *
 * @param[in] ib IronBee engine for logging.
 * @param[in] pool The memory pool to allocate memory out of.
 * @param[in] config Module configuration
 * @param[in] is_dfa Set to true for DFA
 * @param[out] pcpdata Pointer to new struct containing the compilation.
 * @param[in] patt The uncompiled pattern to match.
 * @param[out] errptr Pointer to an error message describing the failure.
 * @param[out] erroffset The location of the failure, if this fails.
 *
 * @returns IronBee status. IB_EINVAL if the pattern is invalid,
 *          IB_EALLOC if memory allocation fails or IB_OK.
 */
static ib_status_t pcre_compile_internal(ib_engine_t *ib,
                                         ib_mpool_t *pool,
                                         const modpcre_cfg_t *config,
                                         bool is_dfa,
                                         modpcre_cpat_data_t **pcpdata,
                                         const char *patt,
                                         const char **errptr,
                                         int *erroffset)
{
    assert(ib != NULL);
    assert(pool != NULL);
    assert(config != NULL);
    assert(pcpdata != NULL);
    assert(patt != NULL);

    /* Pattern data structure we'll create */
    modpcre_cpat_data_t *cpdata;

    /* Compiled pattern. */
    pcre *cpatt = NULL;

    /* Compiled pattern size. Used to copy cpatt. */
    size_t cpatt_sz;

    /* Extra data structure. This contains the study_data pointer. */
    pcre_extra *edata = NULL;

    /* Size of edata->study_data. */
    size_t study_data_sz;

    /* How cpatt is produced. */
    const int compile_flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY;

    /* Are we using JIT? */
    bool use_jit = !is_dfa;

#ifdef PCRE_HAVE_JIT
    if (config->use_jit == 0) {
        use_jit = false;
    }

    /* Do we want to be using JIT? */
    const bool want_jit = use_jit;
#else
    use_jit = false;
#endif /* PCRE_HAVE_JIT */

    cpatt = pcre_compile(patt, compile_flags, errptr, erroffset, NULL);

    if (*errptr != NULL) {
        ib_log_error(ib, "PCRE compile error for \"%s\": %s at offset %d",
                     patt, *errptr, *erroffset);
        return IB_EINVAL;
    }

    if (config->study) {
        if (use_jit) {
#ifdef PCRE_HAVE_JIT
            edata = pcre_study(cpatt, PCRE_STUDY_JIT_COMPILE, errptr);
            if (*errptr != NULL)  {
                pcre_free(cpatt);
                use_jit = false;
                ib_log_warning(ib, "PCRE-JIT study failed: %s", *errptr);
            }
#endif
        }
        else {
            edata = pcre_study(cpatt, 0, errptr);
            if (*errptr != NULL)  {
                pcre_free(cpatt);
                ib_log_error(ib, "PCRE study failed: %s", *errptr);
            }
        }
    }
    else if (use_jit) {
        ib_log_warning(ib, "PCRE: Disabling JIT because study disabled");
        use_jit = false;
    }

#ifdef PCRE_HAVE_JIT
    /* The check to see if JIT compilation was a success changed in 8.20RC1
       now uses pcre_fullinfo see doc/pcrejit.3 */
    if (use_jit) {
        int rc;
        int pcre_jit_ret;

        rc = pcre_fullinfo(cpatt, edata, PCRE_INFO_JIT, &pcre_jit_ret);
        if (rc != 0) {
            ib_log_error(ib, "PCRE-JIT failed to get pcre_fullinfo");
            use_jit = false;
        }
        else if (pcre_jit_ret != 1) {
            ib_log_info(ib, "PCRE-JIT compiler does not support: %s", patt);
            use_jit = false;
        }
        else { /* Assume pcre_jit_ret == 1. */
            /* Do nothing */
        }
    }
    if (want_jit && !use_jit) {
        ib_log_info(ib, "Falling back to normal PCRE");
    }
#endif /*PCRE_HAVE_JIT*/

    /* Compute the size of the populated values of cpatt. */
    pcre_fullinfo(cpatt, edata, PCRE_INFO_SIZE, &cpatt_sz);
    if (edata != NULL) {
        pcre_fullinfo(cpatt, edata, PCRE_INFO_STUDYSIZE, &study_data_sz);
    }
    else {
        study_data_sz = 0;
    }

    /**
     * Below is only allocation and copy operations to pass the PCRE results
     * back to the output variable cpdata.
     */

    cpdata = (modpcre_cpat_data_t *)ib_mpool_calloc(pool, sizeof(*cpdata), 1);
    if (cpdata == NULL) {
        pcre_free(cpatt);
        pcre_free(edata);
        ib_log_error(ib,
                     "Failed to allocate cpdata of size: %zd",
                     sizeof(*cpdata));
        return IB_EALLOC;
    }

    cpdata->is_dfa = is_dfa;
    cpdata->is_jit = use_jit;
    cpdata->cpatt_sz = cpatt_sz;
    cpdata->study_data_sz = study_data_sz;

    /* Copy pattern. */
    cpdata->patt = ib_mpool_strdup(pool, patt);
    if (cpdata->patt == NULL) {
        pcre_free(cpatt);
        pcre_free(edata);
        ib_log_error(ib, "Failed to duplicate pattern string: %s", patt);
        return IB_EALLOC;
    }

    /* Copy compiled pattern. */
    cpdata->cpatt = ib_mpool_memdup(pool, cpatt, cpatt_sz);
    pcre_free(cpatt);
    if (cpdata->cpatt == NULL) {
        pcre_free(edata);
        ib_log_error(ib, "Failed to duplicate pattern of size: %zd", cpatt_sz);
        return IB_EALLOC;
    }
    ib_log_debug(ib, "PCRE copied cpatt @ %p -> %p (%zd bytes)",
                 (void *)cpatt, (void *)cpdata->cpatt, cpatt_sz);

    /* Copy extra data (study data). */
    if (edata != NULL) {

        /* Copy edata. */
        cpdata->edata = ib_mpool_memdup(pool, edata, sizeof(*edata));
        if (cpdata->edata == NULL) {
            pcre_free(edata);
            ib_log_error(ib, "Failed to duplicate edata.");
            return IB_EALLOC;
        }

        /* Copy edata->study_data. */
        if (edata->study_data != NULL) {
            cpdata->edata->study_data =
                ib_mpool_memdup(pool, edata->study_data, study_data_sz);

            if (cpdata->edata->study_data == NULL) {
                ib_log_error(ib, "Failed to study data of size: %zd",
                             study_data_sz);
                pcre_free(edata);
                return IB_EALLOC;
            }
        }
        pcre_free(edata);
    }
    else {
        cpdata->edata = ib_mpool_calloc(pool, 1, sizeof(*edata));
        if (cpdata->edata == NULL) {
            pcre_free(edata);
            ib_log_error(ib, "Failed to allocate edata.");
            return IB_EALLOC;
        }
    }

    /* Set the PCRE limits for non-DFA patterns */
    if (! is_dfa) {
        cpdata->edata->flags |=
            (PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION);
        cpdata->edata->match_limit =
            (unsigned long)config->match_limit;
        cpdata->edata->match_limit_recursion =
            (unsigned long)config->match_limit_recursion;
        cpdata->dfa_ws_size = 0;
    }
    else {
        cpdata->edata->match_limit = 0U;
        cpdata->edata->match_limit_recursion = 0U;
        cpdata->dfa_ws_size = (int)config->dfa_workspace_size;
    }

    /* Set stack limits for JIT */
    if (cpdata->is_jit) {
#ifdef PCRE_HAVE_JIT
        if (config->jit_stack_start == 0U) {
            cpdata->jit_stack_start =
                PCRE_JIT_STACK_START_MULT * config->match_limit_recursion;
        }
        else {
            cpdata->jit_stack_start = (int)config->jit_stack_start;
        }
        if (config->jit_stack_max == 0U) {
            cpdata->jit_stack_max =
                PCRE_JIT_STACK_MAX_MULT * config->match_limit_recursion;
        }
        else {
            cpdata->jit_stack_max = (int)config->jit_stack_max;
        }
#endif
    }
    else {
        cpdata->jit_stack_start = 0;
        cpdata->jit_stack_max = 0;
    }

    ib_log_trace(ib,
                 "Compiled pcre pattern \"%s\": "
                 "cpatt=%p edata=%p limit=%ld rlimit=%ld study=%p "
                 "dfa=%s dfa-ws-sz=%d "
                 "jit=%s jit-stack: start=%d max=%d",
                 patt,
                 (void *)cpdata->cpatt,
                 (void *)cpdata->edata,
                 cpdata->edata->match_limit,
                 cpdata->edata->match_limit_recursion,
                 cpdata->edata->study_data,
                 cpdata->is_dfa ? "yes" : "no",
                 cpdata->dfa_ws_size,
                 cpdata->is_jit ? "yes" : "no",
                 cpdata->jit_stack_start,
                 cpdata->jit_stack_max);
    *pcpdata = cpdata;

    return IB_OK;
}
/**
 * Handle managed collection registration for vars "name=value" parameters
 *
 * @param[in] ib Engine
 * @param[in] module Collection manager's module object (unused)
 * @param[in] manager The collection manager object to register with (unused)
 * @param[in] mp Memory pool to use for allocations
 * @param[in] collection_name Name of the collection
 * @param[in] uri Full collection URI (unused)
 * @param[in] uri_scheme URI scheme (unused)
 * @param[in] uri_data Hierarchical/data part of the URI
 * @param[in] params List of parameter strings
 * @param[in] register_data Register callback data (unused)
 * @param[out] pmanager_inst_data Pointer to manager specific collection data
 *
 * @returns Status code:
 *   - IB_OK All OK
 *   - IB_Exxx Other error
 */
static ib_status_t core_managed_collection_vars_register_fn(
    const ib_engine_t             *ib,
    const ib_module_t             *module,
    const ib_collection_manager_t *manager,
    ib_mpool_t                    *mp,
    const char                    *collection_name,
    const char                    *uri,
    const char                    *uri_scheme,
    const char                    *uri_data,
    const ib_list_t               *params,
    void                          *register_data,
    void                         **pmanager_inst_data)
{
    assert(ib != NULL);
    assert(module != NULL);
    assert(mp != NULL);
    assert(collection_name != NULL);
    assert(params != NULL);
    assert(pmanager_inst_data != NULL);

    const ib_list_node_t *node;
    ib_list_t *vars_list;
    ib_list_t *field_list;
    ib_mpool_t *tmp = ib_engine_pool_temp_get(ib);
    ib_status_t rc;

    if (strlen(uri_data) != 0) {
        return IB_DECLINED;
    }
    if (ib_list_elements(params) < 1) {
        return IB_EINVAL;
    }

    /* Create a temporary list */
    rc = ib_list_create(&vars_list, tmp);
    if (rc != IB_OK) {
        return rc;
    }

    /* First pass; walk through all params, look for "a=b" type syntax */
    IB_LIST_LOOP_CONST(params, node) {
        static const int ovecsize = 9;
        int ovector[ovecsize];
        const char *param = (const char *)node->data;
        core_vars_t *vars;
        int pcre_rc;

        pcre_rc = pcre_exec(core_vars_manager.pattern, NULL,
                            param, strlen(param),
                            0, 0, ovector, ovecsize);
        if (pcre_rc < 0) {
            return IB_DECLINED;
        }

        vars = ib_mpool_alloc(tmp, sizeof(*vars));
        if (vars == NULL) {
            return IB_EALLOC;
        }
        vars->name  = ib_mpool_memdup_to_str(tmp,
                                             param + ovector[2],
                                             ovector[3] - ovector[2]);
        vars->value = ib_mpool_memdup_to_str(tmp,
                                             param + ovector[4],
                                             ovector[5] - ovector[4]);
        if ( (vars->name == NULL) || (vars->value == NULL) ) {
            return IB_EALLOC;
        }
        rc = ib_list_push(vars_list, vars);
        if (rc != IB_OK) {
            return rc;
        }
    }

    /* Build the list of fields */
    rc = ib_list_create(&field_list, mp);
    if (rc != IB_OK) {
        return rc;
    }

    /* Now walk though the list, creating a field for each one */
    IB_LIST_LOOP_CONST(vars_list, node) {
        const core_vars_t *vars = (const core_vars_t *)node->data;
        ib_field_t *field;
        ib_field_val_union_t fval;

        rc = ib_field_from_string(mp,
                                  IB_FIELD_NAME(vars->name), vars->value,
                                  &field, &fval);
        if (rc != IB_OK) {
            ib_log_error(ib, "Error creating field (\"%s\", \"%s\"): %s",
                         vars->name, vars->value,
                         ib_status_to_string(rc));
            return rc;
        }
        rc = ib_list_push(field_list, field);
        if (rc != IB_OK) {
            return rc;
        }

        if (field->type == IB_FTYPE_NUM) {
            ib_log_trace(ib, "Created numeric field \"%s\" %"PRId64" in \"%s\"",
                         vars->name, fval.num, collection_name);
        }

        else if (field->type == IB_FTYPE_FLOAT) {
            ib_log_trace(ib, "Created float field \"%s\" %f in \"%s\"",
                         vars->name, (double)fval.fnum, collection_name);
        }
        else {
            ib_log_trace(ib, "Created string field \"%s\" \"%s\" in \"%s\"",
                         vars->name, fval.nulstr, collection_name);
        }
    }

    /* Finally, store the list as the manager specific collection data */
    *pmanager_inst_data = field_list;

    return IB_OK;
}