|  | 
 | /* | 
 |  * Copyright (C) Igor Sysoev | 
 |  * Copyright (C) Nginx, Inc. | 
 |  */ | 
 |  | 
 |  | 
 | #include <ngx_config.h> | 
 | #include <ngx_core.h> | 
 |  | 
 |  | 
 | typedef struct { | 
 |     ngx_flag_t  pcre_jit; | 
 | } ngx_regex_conf_t; | 
 |  | 
 |  | 
 | static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); | 
 | static void ngx_libc_cdecl ngx_regex_free(void *p); | 
 | #if (NGX_HAVE_PCRE_JIT) | 
 | static void ngx_pcre_free_studies(void *data); | 
 | #endif | 
 |  | 
 | static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle); | 
 |  | 
 | static void *ngx_regex_create_conf(ngx_cycle_t *cycle); | 
 | static char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf); | 
 |  | 
 | static char *ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data); | 
 | static ngx_conf_post_t  ngx_regex_pcre_jit_post = { ngx_regex_pcre_jit }; | 
 |  | 
 |  | 
 | static ngx_command_t  ngx_regex_commands[] = { | 
 |  | 
 |     { ngx_string("pcre_jit"), | 
 |       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, | 
 |       ngx_conf_set_flag_slot, | 
 |       0, | 
 |       offsetof(ngx_regex_conf_t, pcre_jit), | 
 |       &ngx_regex_pcre_jit_post }, | 
 |  | 
 |       ngx_null_command | 
 | }; | 
 |  | 
 |  | 
 | static ngx_core_module_t  ngx_regex_module_ctx = { | 
 |     ngx_string("regex"), | 
 |     ngx_regex_create_conf, | 
 |     ngx_regex_init_conf | 
 | }; | 
 |  | 
 |  | 
 | ngx_module_t  ngx_regex_module = { | 
 |     NGX_MODULE_V1, | 
 |     &ngx_regex_module_ctx,                 /* module context */ | 
 |     ngx_regex_commands,                    /* module directives */ | 
 |     NGX_CORE_MODULE,                       /* module type */ | 
 |     NULL,                                  /* init master */ | 
 |     ngx_regex_module_init,                 /* init module */ | 
 |     NULL,                                  /* init process */ | 
 |     NULL,                                  /* init thread */ | 
 |     NULL,                                  /* exit thread */ | 
 |     NULL,                                  /* exit process */ | 
 |     NULL,                                  /* exit master */ | 
 |     NGX_MODULE_V1_PADDING | 
 | }; | 
 |  | 
 |  | 
 | static ngx_pool_t  *ngx_pcre_pool; | 
 | static ngx_list_t  *ngx_pcre_studies; | 
 |  | 
 |  | 
 | void | 
 | ngx_regex_init(void) | 
 | { | 
 |     pcre_malloc = ngx_regex_malloc; | 
 |     pcre_free = ngx_regex_free; | 
 | } | 
 |  | 
 |  | 
 | static ngx_inline void | 
 | ngx_regex_malloc_init(ngx_pool_t *pool) | 
 | { | 
 | #if (NGX_THREADS) | 
 |     ngx_core_tls_t  *tls; | 
 |  | 
 |     if (ngx_threaded) { | 
 |         tls = ngx_thread_get_tls(ngx_core_tls_key); | 
 |         tls->pool = pool; | 
 |         return; | 
 |     } | 
 |  | 
 | #endif | 
 |  | 
 |     ngx_pcre_pool = pool; | 
 | } | 
 |  | 
 |  | 
 | static ngx_inline void | 
 | ngx_regex_malloc_done(void) | 
 | { | 
 | #if (NGX_THREADS) | 
 |     ngx_core_tls_t  *tls; | 
 |  | 
 |     if (ngx_threaded) { | 
 |         tls = ngx_thread_get_tls(ngx_core_tls_key); | 
 |         tls->pool = NULL; | 
 |         return; | 
 |     } | 
 |  | 
 | #endif | 
 |  | 
 |     ngx_pcre_pool = NULL; | 
 | } | 
 |  | 
 |  | 
 | ngx_int_t | 
 | ngx_regex_compile(ngx_regex_compile_t *rc) | 
 | { | 
 |     int               n, erroff; | 
 |     char             *p; | 
 |     pcre             *re; | 
 |     const char       *errstr; | 
 |     ngx_regex_elt_t  *elt; | 
 |  | 
 |     ngx_regex_malloc_init(rc->pool); | 
 |  | 
 |     re = pcre_compile((const char *) rc->pattern.data, (int) rc->options, | 
 |                       &errstr, &erroff, NULL); | 
 |  | 
 |     /* ensure that there is no current pool */ | 
 |     ngx_regex_malloc_done(); | 
 |  | 
 |     if (re == NULL) { | 
 |         if ((size_t) erroff == rc->pattern.len) { | 
 |            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, | 
 |                               "pcre_compile() failed: %s in \"%V\"", | 
 |                                errstr, &rc->pattern) | 
 |                       - rc->err.data; | 
 |  | 
 |         } else { | 
 |            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, | 
 |                               "pcre_compile() failed: %s in \"%V\" at \"%s\"", | 
 |                                errstr, &rc->pattern, rc->pattern.data + erroff) | 
 |                       - rc->err.data; | 
 |         } | 
 |  | 
 |         return NGX_ERROR; | 
 |     } | 
 |  | 
 |     rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t)); | 
 |     if (rc->regex == NULL) { | 
 |         return NGX_ERROR; | 
 |     } | 
 |  | 
 |     rc->regex->code = re; | 
 |  | 
 |     /* do not study at runtime */ | 
 |  | 
 |     if (ngx_pcre_studies != NULL) { | 
 |         elt = ngx_list_push(ngx_pcre_studies); | 
 |         if (elt == NULL) { | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |         elt->regex = rc->regex; | 
 |         elt->name = rc->pattern.data; | 
 |     } | 
 |  | 
 |     n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures); | 
 |     if (n < 0) { | 
 |         p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d"; | 
 |         goto failed; | 
 |     } | 
 |  | 
 |     if (rc->captures == 0) { | 
 |         return NGX_OK; | 
 |     } | 
 |  | 
 |     n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures); | 
 |     if (n < 0) { | 
 |         p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d"; | 
 |         goto failed; | 
 |     } | 
 |  | 
 |     if (rc->named_captures == 0) { | 
 |         return NGX_OK; | 
 |     } | 
 |  | 
 |     n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size); | 
 |     if (n < 0) { | 
 |         p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d"; | 
 |         goto failed; | 
 |     } | 
 |  | 
 |     n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names); | 
 |     if (n < 0) { | 
 |         p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d"; | 
 |         goto failed; | 
 |     } | 
 |  | 
 |     return NGX_OK; | 
 |  | 
 | failed: | 
 |  | 
 |     rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) | 
 |                   - rc->err.data; | 
 |     return NGX_OK; | 
 | } | 
 |  | 
 |  | 
 | ngx_int_t | 
 | ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) | 
 | { | 
 |     ngx_int_t         n; | 
 |     ngx_uint_t        i; | 
 |     ngx_regex_elt_t  *re; | 
 |  | 
 |     re = a->elts; | 
 |  | 
 |     for (i = 0; i < a->nelts; i++) { | 
 |  | 
 |         n = ngx_regex_exec(re[i].regex, s, NULL, 0); | 
 |  | 
 |         if (n == NGX_REGEX_NO_MATCHED) { | 
 |             continue; | 
 |         } | 
 |  | 
 |         if (n < 0) { | 
 |             ngx_log_error(NGX_LOG_ALERT, log, 0, | 
 |                           ngx_regex_exec_n " failed: %i on \"%V\" using \"%s\"", | 
 |                           n, s, re[i].name); | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |         /* match */ | 
 |  | 
 |         return NGX_OK; | 
 |     } | 
 |  | 
 |     return NGX_DECLINED; | 
 | } | 
 |  | 
 |  | 
 | static void * ngx_libc_cdecl | 
 | ngx_regex_malloc(size_t size) | 
 | { | 
 |     ngx_pool_t      *pool; | 
 | #if (NGX_THREADS) | 
 |     ngx_core_tls_t  *tls; | 
 |  | 
 |     if (ngx_threaded) { | 
 |         tls = ngx_thread_get_tls(ngx_core_tls_key); | 
 |         pool = tls->pool; | 
 |  | 
 |     } else { | 
 |         pool = ngx_pcre_pool; | 
 |     } | 
 |  | 
 | #else | 
 |  | 
 |     pool = ngx_pcre_pool; | 
 |  | 
 | #endif | 
 |  | 
 |     if (pool) { | 
 |         return ngx_palloc(pool, size); | 
 |     } | 
 |  | 
 |     return NULL; | 
 | } | 
 |  | 
 |  | 
 | static void ngx_libc_cdecl | 
 | ngx_regex_free(void *p) | 
 | { | 
 |     return; | 
 | } | 
 |  | 
 |  | 
 | #if (NGX_HAVE_PCRE_JIT) | 
 |  | 
 | static void | 
 | ngx_pcre_free_studies(void *data) | 
 | { | 
 |     ngx_list_t *studies = data; | 
 |  | 
 |     ngx_uint_t        i; | 
 |     ngx_list_part_t  *part; | 
 |     ngx_regex_elt_t  *elts; | 
 |  | 
 |     part = &studies->part; | 
 |     elts = part->elts; | 
 |  | 
 |     for (i = 0 ; /* void */ ; i++) { | 
 |  | 
 |         if (i >= part->nelts) { | 
 |             if (part->next == NULL) { | 
 |                 break; | 
 |             } | 
 |  | 
 |             part = part->next; | 
 |             elts = part->elts; | 
 |             i = 0; | 
 |         } | 
 |  | 
 |         if (elts[i].regex->extra != NULL) { | 
 |             pcre_free_study(elts[i].regex->extra); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 |  | 
 | static ngx_int_t | 
 | ngx_regex_module_init(ngx_cycle_t *cycle) | 
 | { | 
 |     int               opt; | 
 |     const char       *errstr; | 
 |     ngx_uint_t        i; | 
 |     ngx_list_part_t  *part; | 
 |     ngx_regex_elt_t  *elts; | 
 |  | 
 |     opt = 0; | 
 |  | 
 | #if (NGX_HAVE_PCRE_JIT) | 
 |     { | 
 |     ngx_regex_conf_t    *rcf; | 
 |     ngx_pool_cleanup_t  *cln; | 
 |  | 
 |     rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module); | 
 |  | 
 |     if (rcf->pcre_jit) { | 
 |         opt = PCRE_STUDY_JIT_COMPILE; | 
 |  | 
 |         /* | 
 |          * The PCRE JIT compiler uses mmap for its executable codes, so we | 
 |          * have to explicitly call the pcre_free_study() function to free | 
 |          * this memory. | 
 |          */ | 
 |  | 
 |         cln = ngx_pool_cleanup_add(cycle->pool, 0); | 
 |         if (cln == NULL) { | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |         cln->handler = ngx_pcre_free_studies; | 
 |         cln->data = ngx_pcre_studies; | 
 |     } | 
 |     } | 
 | #endif | 
 |  | 
 |     ngx_regex_malloc_init(cycle->pool); | 
 |  | 
 |     part = &ngx_pcre_studies->part; | 
 |     elts = part->elts; | 
 |  | 
 |     for (i = 0 ; /* void */ ; i++) { | 
 |  | 
 |         if (i >= part->nelts) { | 
 |             if (part->next == NULL) { | 
 |                 break; | 
 |             } | 
 |  | 
 |             part = part->next; | 
 |             elts = part->elts; | 
 |             i = 0; | 
 |         } | 
 |  | 
 |         elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr); | 
 |  | 
 |         if (errstr != NULL) { | 
 |             ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
 |                           "pcre_study() failed: %s in \"%s\"", | 
 |                           errstr, elts[i].name); | 
 |         } | 
 |  | 
 | #if (NGX_HAVE_PCRE_JIT) | 
 |         if (opt & PCRE_STUDY_JIT_COMPILE) { | 
 |             int jit, n; | 
 |  | 
 |             jit = 0; | 
 |             n = pcre_fullinfo(elts[i].regex->code, elts[i].regex->extra, | 
 |                               PCRE_INFO_JIT, &jit); | 
 |  | 
 |             if (n != 0 || jit != 1) { | 
 |                 ngx_log_error(NGX_LOG_INFO, cycle->log, 0, | 
 |                               "JIT compiler does not support pattern: \"%s\"", | 
 |                               elts[i].name); | 
 |             } | 
 |         } | 
 | #endif | 
 |     } | 
 |  | 
 |     ngx_regex_malloc_done(); | 
 |  | 
 |     ngx_pcre_studies = NULL; | 
 |  | 
 |     return NGX_OK; | 
 | } | 
 |  | 
 |  | 
 | static void * | 
 | ngx_regex_create_conf(ngx_cycle_t *cycle) | 
 | { | 
 |     ngx_regex_conf_t  *rcf; | 
 |  | 
 |     rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t)); | 
 |     if (rcf == NULL) { | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     rcf->pcre_jit = NGX_CONF_UNSET; | 
 |  | 
 |     ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); | 
 |     if (ngx_pcre_studies == NULL) { | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     return rcf; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf) | 
 | { | 
 |     ngx_regex_conf_t *rcf = conf; | 
 |  | 
 |     ngx_conf_init_value(rcf->pcre_jit, 0); | 
 |  | 
 |     return NGX_CONF_OK; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data) | 
 | { | 
 |     ngx_flag_t  *fp = data; | 
 |  | 
 |     if (*fp == 0) { | 
 |         return NGX_CONF_OK; | 
 |     } | 
 |  | 
 | #if (NGX_HAVE_PCRE_JIT) | 
 |     { | 
 |     int  jit, r; | 
 |  | 
 |     jit = 0; | 
 |     r = pcre_config(PCRE_CONFIG_JIT, &jit); | 
 |  | 
 |     if (r != 0 || jit != 1) { | 
 |         ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 
 |                            "PCRE library does not support JIT"); | 
 |         *fp = 0; | 
 |     } | 
 |     } | 
 | #else | 
 |     ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 
 |                        "nginx was built without PCRE JIT support"); | 
 |     *fp = 0; | 
 | #endif | 
 |  | 
 |     return NGX_CONF_OK; | 
 | } |