00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 #include "apr.h"
00084 #include "apr_strings.h"
00085 #include "apr_strmatch.h"
00086
00087 #define APR_WANT_STRFUNC
00088 #include "apr_want.h"
00089
00090 #include "ap_config.h"
00091 #include "httpd.h"
00092 #include "http_config.h"
00093 #include "http_core.h"
00094 #include "http_log.h"
00095 #include "http_protocol.h"
00096
00097
00098 enum special {
00099 SPECIAL_NOT,
00100 SPECIAL_REMOTE_ADDR,
00101 SPECIAL_REMOTE_HOST,
00102 SPECIAL_REQUEST_URI,
00103 SPECIAL_REQUEST_METHOD,
00104 SPECIAL_REQUEST_PROTOCOL,
00105 SPECIAL_SERVER_ADDR
00106 };
00107 typedef struct {
00108 char *name;
00109 regex_t *pnamereg;
00110 char *regex;
00111 regex_t *preg;
00112 const apr_strmatch_pattern *pattern;
00113 apr_table_t *features;
00114 enum special special_type;
00115 int icase;
00116 } sei_entry;
00117
00118 typedef struct {
00119 apr_array_header_t *conditionals;
00120 } sei_cfg_rec;
00121
00122 module AP_MODULE_DECLARE_DATA setenvif_module;
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 static void *create_setenvif_config(apr_pool_t *p)
00134 {
00135 sei_cfg_rec *new = (sei_cfg_rec *) apr_palloc(p, sizeof(sei_cfg_rec));
00136
00137 new->conditionals = apr_array_make(p, 20, sizeof(sei_entry));
00138 return (void *) new;
00139 }
00140
00141 static void *create_setenvif_config_svr(apr_pool_t *p, server_rec *dummy)
00142 {
00143 return create_setenvif_config(p);
00144 }
00145
00146 static void *create_setenvif_config_dir(apr_pool_t *p, char *dummy)
00147 {
00148 return create_setenvif_config(p);
00149 }
00150
00151 static void *merge_setenvif_config(apr_pool_t *p, void *basev, void *overridesv)
00152 {
00153 sei_cfg_rec *a = apr_pcalloc(p, sizeof(sei_cfg_rec));
00154 sei_cfg_rec *base = basev, *overrides = overridesv;
00155
00156 a->conditionals = apr_array_append(p, base->conditionals,
00157 overrides->conditionals);
00158 return a;
00159 }
00160
00161
00162
00163
00164
00165 #define ICASE_MAGIC ((void *)(&setenvif_module))
00166 #define SEI_MAGIC_HEIRLOOM "setenvif-phase-flag"
00167
00168 static int is_header_regex(apr_pool_t *p, const char* name)
00169 {
00170
00171
00172
00173
00174 regex_t *preg = ap_pregcomp(p, "^[-A-Za-z0-9_]*$",
00175 (REG_EXTENDED | REG_NOSUB ));
00176 ap_assert(preg != NULL);
00177
00178 if (ap_regexec(preg, name, 0, NULL, 0)) {
00179 return 1;
00180 }
00181
00182 return 0;
00183 }
00184
00185
00186
00187
00188
00189
00190
00191
00192 static const char *non_regex_pattern(apr_pool_t *p, const char *s)
00193 {
00194 const char *src = s;
00195 int escapes_found = 0;
00196 int in_escape = 0;
00197
00198 while (*src) {
00199 switch (*src) {
00200 case '^':
00201 case '.':
00202 case '$':
00203 case '|':
00204 case '(':
00205 case ')':
00206 case '[':
00207 case ']':
00208 case '*':
00209 case '+':
00210 case '?':
00211 case '{':
00212 case '}':
00213 if (!in_escape) {
00214 return NULL;
00215 }
00216 in_escape = 0;
00217 break;
00218 case '\\':
00219 if (!in_escape) {
00220 in_escape = 1;
00221 escapes_found = 1;
00222 }
00223 else {
00224 in_escape = 0;
00225 }
00226 break;
00227 default:
00228 if (in_escape) {
00229 return NULL;
00230 }
00231 break;
00232 }
00233 src++;
00234 }
00235 if (!escapes_found) {
00236 return s;
00237 }
00238 else {
00239 char *unescaped = (char *)apr_palloc(p, src - s + 1);
00240 char *dst = unescaped;
00241 src = s;
00242 do {
00243 if (*src == '\\') {
00244 src++;
00245 }
00246 } while ((*dst++ = *src++));
00247 return unescaped;
00248 }
00249 }
00250
00251 static const char *add_setenvif_core(cmd_parms *cmd, void *mconfig,
00252 char *fname, const char *args)
00253 {
00254 char *regex;
00255 const char *simple_pattern;
00256 const char *feature;
00257 sei_cfg_rec *sconf;
00258 sei_entry *new;
00259 sei_entry *entries;
00260 char *var;
00261 int i;
00262 int beenhere = 0;
00263 int icase;
00264
00265
00266
00267
00268
00269
00270 sconf = (cmd->path != NULL)
00271 ? (sei_cfg_rec *) mconfig
00272 : (sei_cfg_rec *) ap_get_module_config(cmd->server->module_config,
00273 &setenvif_module);
00274 entries = (sei_entry *) sconf->conditionals->elts;
00275
00276 regex = ap_getword_conf(cmd->pool, &args);
00277 if (!*regex) {
00278 return apr_pstrcat(cmd->pool, "Missing regular expression for ",
00279 cmd->cmd->name, NULL);
00280 }
00281
00282
00283
00284
00285
00286
00287 for (i = 0; i < sconf->conditionals->nelts; ++i) {
00288 new = &entries[i];
00289 if (!strcasecmp(new->name, fname)) {
00290 fname = new->name;
00291 break;
00292 }
00293 }
00294
00295
00296
00297
00298 i = sconf->conditionals->nelts - 1;
00299 icase = cmd->info == ICASE_MAGIC;
00300 if (i < 0
00301 || entries[i].name != fname
00302 || entries[i].icase != icase
00303 || strcmp(entries[i].regex, regex)) {
00304
00305
00306 new = apr_array_push(sconf->conditionals);
00307 new->name = fname;
00308 new->regex = regex;
00309 new->icase = icase;
00310 if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
00311 new->pattern = apr_strmatch_precompile(cmd->pool,
00312 simple_pattern, !icase);
00313 if (new->pattern == NULL) {
00314 return apr_pstrcat(cmd->pool, cmd->cmd->name,
00315 " pattern could not be compiled.", NULL);
00316 }
00317 new->preg = NULL;
00318 }
00319 else {
00320 new->preg = ap_pregcomp(cmd->pool, regex,
00321 (REG_EXTENDED | (icase ? REG_ICASE : 0)));
00322 if (new->preg == NULL) {
00323 return apr_pstrcat(cmd->pool, cmd->cmd->name,
00324 " regex could not be compiled.", NULL);
00325 }
00326 new->pattern = NULL;
00327 }
00328 new->features = apr_table_make(cmd->pool, 2);
00329
00330 if (!strcasecmp(fname, "remote_addr")) {
00331 new->special_type = SPECIAL_REMOTE_ADDR;
00332 }
00333 else if (!strcasecmp(fname, "remote_host")) {
00334 new->special_type = SPECIAL_REMOTE_HOST;
00335 }
00336 else if (!strcasecmp(fname, "request_uri")) {
00337 new->special_type = SPECIAL_REQUEST_URI;
00338 }
00339 else if (!strcasecmp(fname, "request_method")) {
00340 new->special_type = SPECIAL_REQUEST_METHOD;
00341 }
00342 else if (!strcasecmp(fname, "request_protocol")) {
00343 new->special_type = SPECIAL_REQUEST_PROTOCOL;
00344 }
00345 else if (!strcasecmp(fname, "server_addr")) {
00346 new->special_type = SPECIAL_SERVER_ADDR;
00347 }
00348 else {
00349 new->special_type = SPECIAL_NOT;
00350
00351
00352
00353
00354
00355 if (is_header_regex(cmd->pool, fname)) {
00356 new->pnamereg = ap_pregcomp(cmd->pool, fname,
00357 (REG_EXTENDED | REG_NOSUB
00358 | (icase ? REG_ICASE : 0)));
00359 if (new->pnamereg == NULL)
00360 return apr_pstrcat(cmd->pool, cmd->cmd->name,
00361 "Header name regex could not be "
00362 "compiled.", NULL);
00363 }
00364 else {
00365 new->pnamereg = NULL;
00366 }
00367 }
00368 }
00369 else {
00370 new = &entries[i];
00371 }
00372
00373 for ( ; ; ) {
00374 feature = ap_getword_conf(cmd->pool, &args);
00375 if (!*feature) {
00376 break;
00377 }
00378 beenhere++;
00379
00380 var = ap_getword(cmd->pool, &feature, '=');
00381 if (*feature) {
00382 apr_table_setn(new->features, var, feature);
00383 }
00384 else if (*var == '!') {
00385 apr_table_setn(new->features, var + 1, "!");
00386 }
00387 else {
00388 apr_table_setn(new->features, var, "1");
00389 }
00390 }
00391
00392 if (!beenhere) {
00393 return apr_pstrcat(cmd->pool, "Missing envariable expression for ",
00394 cmd->cmd->name, NULL);
00395 }
00396
00397 return NULL;
00398 }
00399
00400 static const char *add_setenvif(cmd_parms *cmd, void *mconfig,
00401 const char *args)
00402 {
00403 char *fname;
00404
00405
00406 fname = ap_getword_conf(cmd->pool, &args);
00407 if (!*fname) {
00408 return apr_pstrcat(cmd->pool, "Missing header-field name for ",
00409 cmd->cmd->name, NULL);
00410 }
00411 return add_setenvif_core(cmd, mconfig, fname, args);
00412 }
00413
00414
00415
00416
00417
00418
00419 static const char *add_browser(cmd_parms *cmd, void *mconfig, const char *args)
00420 {
00421 return add_setenvif_core(cmd, mconfig, "User-Agent", args);
00422 }
00423
00424 static const command_rec setenvif_module_cmds[] =
00425 {
00426 AP_INIT_RAW_ARGS("SetEnvIf", add_setenvif, NULL, OR_FILEINFO,
00427 "A header-name, regex and a list of variables."),
00428 AP_INIT_RAW_ARGS("SetEnvIfNoCase", add_setenvif, ICASE_MAGIC, OR_FILEINFO,
00429 "a header-name, regex and a list of variables."),
00430 AP_INIT_RAW_ARGS("BrowserMatch", add_browser, NULL, OR_FILEINFO,
00431 "A browser regex and a list of variables."),
00432 AP_INIT_RAW_ARGS("BrowserMatchNoCase", add_browser, ICASE_MAGIC,
00433 OR_FILEINFO,
00434 "A browser regex and a list of variables."),
00435 { NULL },
00436 };
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447 static int match_headers(request_rec *r)
00448 {
00449 sei_cfg_rec *sconf;
00450 sei_entry *entries;
00451 const apr_table_entry_t *elts;
00452 const char *val;
00453 apr_size_t val_len = 0;
00454 int i, j;
00455 char *last_name;
00456 regmatch_t regm[AP_MAX_REG_MATCH];
00457
00458 if (!ap_get_module_config(r->request_config, &setenvif_module)) {
00459 ap_set_module_config(r->request_config, &setenvif_module,
00460 SEI_MAGIC_HEIRLOOM);
00461 sconf = (sei_cfg_rec *) ap_get_module_config(r->server->module_config,
00462 &setenvif_module);
00463 }
00464 else {
00465 sconf = (sei_cfg_rec *) ap_get_module_config(r->per_dir_config,
00466 &setenvif_module);
00467 }
00468 entries = (sei_entry *) sconf->conditionals->elts;
00469 last_name = NULL;
00470 val = NULL;
00471 for (i = 0; i < sconf->conditionals->nelts; ++i) {
00472 sei_entry *b = &entries[i];
00473
00474
00475
00476
00477
00478
00479 if (b->name != last_name) {
00480 last_name = b->name;
00481 switch (b->special_type) {
00482 case SPECIAL_REMOTE_ADDR:
00483 val = r->connection->remote_ip;
00484 break;
00485 case SPECIAL_SERVER_ADDR:
00486 val = r->connection->local_ip;
00487 break;
00488 case SPECIAL_REMOTE_HOST:
00489 val = ap_get_remote_host(r->connection, r->per_dir_config,
00490 REMOTE_NAME, NULL);
00491 break;
00492 case SPECIAL_REQUEST_URI:
00493 val = r->uri;
00494 break;
00495 case SPECIAL_REQUEST_METHOD:
00496 val = r->method;
00497 break;
00498 case SPECIAL_REQUEST_PROTOCOL:
00499 val = r->protocol;
00500 break;
00501 case SPECIAL_NOT:
00502 if (b->pnamereg) {
00503
00504
00505
00506
00507 const apr_array_header_t
00508 *arr = apr_table_elts(r->headers_in);
00509
00510 elts = (const apr_table_entry_t *) arr->elts;
00511 val = NULL;
00512 for (j = 0; j < arr->nelts; ++j) {
00513 if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {
00514 val = elts[j].val;
00515 }
00516 }
00517 }
00518 else {
00519
00520 val = apr_table_get(r->headers_in, b->name);
00521 if (val == NULL) {
00522 val = apr_table_get(r->subprocess_env, b->name);
00523 }
00524 }
00525 }
00526 val_len = val ? strlen(val) : 0;
00527 }
00528
00529
00530
00531
00532
00533
00534
00535 if (val == NULL) {
00536 val = "";
00537 val_len = 0;
00538 }
00539
00540 if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
00541 (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
00542 0))) {
00543 const apr_array_header_t *arr = apr_table_elts(b->features);
00544 elts = (const apr_table_entry_t *) arr->elts;
00545
00546 for (j = 0; j < arr->nelts; ++j) {
00547 if (*(elts[j].val) == '!') {
00548 apr_table_unset(r->subprocess_env, elts[j].key);
00549 }
00550 else {
00551 if (!b->pattern) {
00552 char *replaced = ap_pregsub(r->pool, elts[j].val, val,
00553 AP_MAX_REG_MATCH, regm);
00554 if (replaced) {
00555 apr_table_setn(r->subprocess_env, elts[j].key,
00556 replaced);
00557 }
00558 }
00559 else {
00560 apr_table_setn(r->subprocess_env, elts[j].key,
00561 elts[j].val);
00562 }
00563 }
00564 }
00565 }
00566 }
00567
00568 return DECLINED;
00569 }
00570
00571 static void register_hooks(apr_pool_t *p)
00572 {
00573 ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
00574 ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
00575 }
00576
00577 module AP_MODULE_DECLARE_DATA setenvif_module =
00578 {
00579 STANDARD20_MODULE_STUFF,
00580 create_setenvif_config_dir,
00581 merge_setenvif_config,
00582 create_setenvif_config_svr,
00583 merge_setenvif_config,
00584 setenvif_module_cmds,
00585 register_hooks
00586 };